Customize the 3D Secure UI

    Control how 3D Secure appears in your web payment flow.

    When using a PaymentIntent to accept a card payment from a customer, 3D Secure authentication may be required because of Strong Customer Authentication requirements or your fraud rules.

    The easiest way to integrate 3D Secure into your web payment flow is to use handleCardPayment or handleCardAction in Stripe.js, which will show the 3D Secure interface inside of a responsive modal dialog on top of your page.

    If you instead want to display 3D Secure inline with your payment form, or using your own modal dialog, this guide describes how to do so. You will need to embed the 3D Secure authentication UI into your payment flow as an <iframe> to a url provided by Stripe.

    To do so, you will need to:

    1. Confirm the PaymentIntent
    2. Check the PaymentIntent status
    3. Render the 3D Secure iframe
    4. Handle the redirect

    Step 1: Confirm the PaymentIntent

    When your customer is ready to complete their purchase, you confirm the PaymentIntent to begin the process of collecting their payment.

    If you want to control the way in which 3D Secure is displayed, you will need to provide a return_url, which is where the 3D Secure <iframe> will be redirected once authentication is complete. If your site uses a content security policy, check that iframes from https://js.stripe.com, https://hooks.stripe.com, and the origin of the URL you passed to return_url are allowed.

    If you are confirming from the frontend, use the confirmPaymentIntent method in Stripe.js. For example, if your are gathering card information using Stripe Elements:

    stripe.confirmPaymentIntent(
      '{{PAYMENT_INTENT_CLIENT_SECRET}}',
      cardElement,
      {
        return_url: 'https://example.com/return_url'
      }
    ).then(function(result) {
      // Handle result.error or result.paymentIntent
      // More details in Step 2.
    });
    

    If you are confirming from your backend, be sure to provide a return_url. Depending on your integration, you may want to pass other information to confirm as well.

    intent = Stripe::PaymentIntent.confirm(
      '{{PAYMENT_INTENT_ID}}',
      {
        return_url: 'https://example.com/return_url'
      }
    )
    
    intent = stripe.PaymentIntent.confirm(
      '{{PAYMENT_INTENT_ID}}',
      return_url='https://example.com/return_url'
    )
    
    $intent = \Stripe\PaymentIntent::retrieve('{{PAYMENT_INTENT_ID}}');
    $intent->confirm([
        'return_url' => 'https://example.com/return_url',
    ]);
    
    PaymentIntent intent = PaymentIntent.retrieve("{{PAYMENT_INTENT_ID}}");
    Map<String, Object> params = new HashMap<String, Object>();
    params.put("return_url", "https://example.com/return_url");
    intent.confirm(params);
    
    (async () => {
      let intent = await stripe.paymentIntents.confirm(
        '{{PAYMENT_INTENT_ID}}',
        {
          return_url: 'https://example.com/return_url'
        }
      );
    })()
    
    params := &stripe.PaymentIntentConfirmParams{
      ReturnUrl: stripe.String("https://example.com/return_url"),
    }
    intent, err := paymentintent.Confirm("{{PAYMENT_INTENT_ID}}", params)
    
    var service = new PaymentIntentService();
    var options = new PaymentIntentConfirmOptions
    {
      ReturnUrl = "https://example.com/return_url",
    };
    var intent = service.Confirm("{{PAYMENT_INTENT_ID}}", options);
    

    Step 2: Check the PaymentIntent status

    Next, inspect the status property of the confirmed PaymentIntent to determine if the payment completed successfully. The following list describes possible status values and their significance:

    Status Description
    requires_payment_method The request failed with a 402 HTTP status code, meaning that the payment was unsuccessful. Check the last_payment_error property and attempt to try again, collecting new payment information from the customer if necessary.
    requires_action An additional step like 3D Secure is required to complete the payment. Notify the customer to return to your application to complete payment.
    succeeded The payment completed and resulted in the creation of a charge using the supplied payment method. No further steps are required.

    Note that on versions of the API before 2019-02-11, requires_payment_method appears as requires_source and requires_action appears as requires_source_action.

    Step 3: Render the 3D Secure iframe

    When the value of the status property is requires_action, some additional step is required before payment can be processed. For a card payment requiring 3D Secure, the PaymentIntent’s status will be requires_action and its next_action property will be redirect_to_url. The redirect_to_url payload contains a URL that you should open in an iframe to display 3D Secure:

    var iframe = document.createElement('iframe');
    iframe.src = paymentIntent.next_action.redirect_to_url.url;
    iframe.width = 600;
    iframe.height = 400;
    yourContainer.appendChild(iframe);

    For 3D Secure 2, card issuers are required to support showing the 3D Secure content at sizes of 250x400, 390x400, 500x600, 600x400, and full screen (dimensions are width by height). The 3D Secure UI may be better if you open the iframe at exactly one of those sizes.

    Step 4: Handle the redirect

    After the customer completes 3D Secure, the iframe will be redirected to the return_url you provided when confirming the PaymentIntent. That page should postMessage to your top-level page to inform it that 3D Secure authentication is complete. Your top-level page should then determine whether payment was successful or if your customer needs to take further action.

    For example, you might have your return_url page execute:

    window.top.postMessage('3DS-authentication-complete');

    Your top payment page should be listening for this postMessage to know when authentication has finished. You should then retrieve the updated PaymentIntent and check on the status of the payment. If the authentication failed, the PaymentIntent’s status will be requires_payment_method. If the payment completed successfully, it will be succeeded. If you use separate authorize and capture, the status will be requires_capture instead.

    function on3DSComplete() {
      // Hide the 3DS UI
      yourContainer.remove();
    
      // Check the PaymentIntent
      stripe.retrievePaymentIntent('{{PAYMENT_INTENT_CLIENT_SECRET}}')
        .then(function(result) {
          if (result.error) {
            // PaymentIntent client secret was invalid
          } else {
            if (result.paymentIntent.status === 'succeeded') {
              // Show your customer that the payment has succeeded
            } else if (result.paymentIntent.status === 'requires_payment_method') {
              // Authentication failed, prompt the customer to enter another payment method
            }
          }
        });
    }
    
    window.addEventListener('message', function(ev) {
      if (ev.data === '3DS-authentication-complete') {
        on3DSComplete();
      }
    }, false);

    Was this page helpful? Yes No

    Send

    Thank you for helping improve Stripe's documentation. If you need help or have any questions, please consider contacting support.

    Questions?

    We're always happy to help with code or other questions you might have. Search our documentation, contact support, or connect with our sales team. You can also chat live with other developers in #stripe on freenode.

    On this page