Skip to content
Create account
or
Sign in
The Stripe Docs logo
/
Ask AI
Create account
Sign in
Get started
Payments
Finance automation
Platforms and marketplaces
Money management
Developer tools
Get started
Payments
Finance automation
Get started
Payments
Finance automation
Platforms and marketplaces
Money management
Overview
About Stripe payments
Upgrade your integration
Payments analytics
Online payments
OverviewFind your use caseManaged Payments
Use Payment Links
Build a checkout page
Build an advanced integration
Build an in-app integration
    Overview
    Payment Sheet
    Embedded Payment Element
    Link out for in-app purchases
    Collect addresses
    US and Canadian cards
      Save cards without authentication
      Upgrade to handle authentication
Payment methods
Add payment methods
Manage payment methods
Faster checkout with Link
Payment interfaces
Payment Links
Checkout
Web Elements
In-app Elements
Payment scenarios
Custom payment flows
Flexible acquiring
Orchestration
In-person payments
Terminal
Other Stripe products
Financial Connections
Crypto
Climate
HomePaymentsBuild an in-app integrationUS and Canadian cards

Migrate your basic card integration

Migrate your mobile app to an integration that can handle bank requests for card authentication.

Copy page

If you followed the Card payments without bank authentication guide, your integration creates payments that decline when a bank asks the customer to authenticate the purchase.

If you start seeing many failed payments like the one in the Dashboard below or with an error code of requires_action_not_handled in the API, upgrade your basic integration to handle, rather than decline, these payments.

Dashboard showing a failed payment that says that this bank required authentication for this payment

Use this guide to learn how to upgrade the integration you built in the previous guide to add server and client code that prompts the customer to authenticate the payment by displaying a modal.

Note

See a full sample of this integration on GitHub.

Check if the payment requires authentication
Server-side

Make three changes to the endpoint on your server that creates the PaymentIntent:

  1. Remove the error_on_requires_action parameter to no longer fail payments that require authentication. Instead, the PaymentIntent status changes to requires_action.
  2. Add the confirmation_method parameter to indicate that you want to explicitly (manually) confirm the payment again on the server after handling authentication requests.
  3. Add the use_stripe_sdk parameter to enable your mobile client to handle additional authentication steps.
Command Line
curl
curl https://api.stripe.com/v1/payment_intents \ -u
sk_test_BQokikJOvBiI2HlWgH4olfQ2
:
\ -d amount=1099 \ -d currency=usd \ -d payment_method_types[]=card \ -d confirm=true \ -d error_on_requires_action=true \ -d payment_method="{{PAYMENT_METHOD_ID}}" \ -d confirmation_method=manual

Then update your “generate response” function to handle the requires_action state instead of erroring:

Command Line
curl
# If the request succeeds, check the # PaymentIntent's `status` and handle # its `next_action`.

Ask the customer to authenticate
Client-side

Next, update your iOS code to tell Stripe to show a modal if the customer needs to authenticate.

Use handleNextActionForPayment when a PaymentIntent has a status of requires_action. If successful, the PaymentIntent will have a status of requires_confirmation and you need to confirm the PaymentIntent again on your server to finish the payment.

Objective C
- (void)handleResponse:(NSDictionary *)json { if (json[@"error"] != nil) { // Display the error to the customer } else if (json[@"requiresAction"]) { // Payment requires additional actions NSString *clientSecret = json[@"clientSecret"]; STPPaymentHandler *paymentHandler = [STPPaymentHandler sharedHandler]; [paymentHandler handleNextActionForPayment:clientSecret withAuthenticationContext:self returnURL:nil completion:^(STPPaymentHandlerActionStatus status, STPPaymentIntent *paymentIntent, NSError *handleActionError) { switch (status) { case STPPaymentHandlerActionStatusFailed: { // Display handleActionError to the customer break; } case STPPaymentHandlerActionStatusCanceled: { // Canceled break; } case STPPaymentHandlerActionStatusSucceeded: { // The card action has been handled // Send the PaymentIntent ID to your server and confirm it again break; } default: break; } }]; } else { // Display success message } } # pragma mark STPAuthenticationContext - (UIViewController *)authenticationPresentingViewController { return self; }

Confirm the PaymentIntent again
Server-side

Using the same endpoint you set up earlier, confirm the PaymentIntent again to finalize the payment and fulfill the order. The payment attempt fails and transitions back to requires_payment_method if it is not confirmed again within one hour.

Command Line
curl
curl https://api.stripe.com/v1/payment_intents/{{PAYMENT_INTENT_ID}}/confirm \ -u
sk_test_BQokikJOvBiI2HlWgH4olfQ2
:
\ -X "POST"

Test the integration

Use our test cards in a sandbox to verify that your integration was properly updated. Stripe displays a fake authentication page inside the modal in a sandbox that lets you simulate a successful or failed authentication attempt. In live mode the bank controls the UI of what is displayed inside the modal.

NumberDescription
Succeeds and immediately processes the payment.
Always fails with a decline code of insufficient_funds.
Requires authentication, which in this integration will fail with a decline code of authentication_not_handled.
Was this page helpful?
YesNo
Need help? Contact Support.
Join our early access program.
Check out our changelog.
Questions? Contact Sales.
LLM? Read llms.txt.
Powered by Markdoc