Migrating to Payment Intents with Automatic Confirmation

    Learn how to migrate an existing cards and Charges API integration to the Payment Intents API using automatic confirmation.

    Comparison with a traditional Charges API integration

    A traditional token-based Charges API integration that uses Elements to accept card payments typically includes the following steps:

    • Collect the customer’s payment information in the browser with Elements
    • Tokenize the payment information with Stripe.js
    • Perform a request to send the token to your server
    • Use the token to create a charge on your server with the desired amount and currency
    • Fulfill the customer’s order if payment is successful

    By comparison, a Payment Intents API integration using automatic confirmation typically includes the following steps:

    • Create a PaymentIntent on your server with the desired amount and currency
    • Send the PaymentIntent’s client secret to the client side
    • Collect the customer’s payment information in the browser with Elements
    • Use Stripe.js or the mobile SDKs to perform dynamic 3D Secure and complete the payment on the client side
    • Use webhooks on your server to fulfill the customer’s order if the payment is successful

    Strategy for migration

    We understand that migrating your payment flow can be daunting. It is safe to incrementally adopt the Payment Intents API and use it in parallel with the Charges API. To this end, we suggest splitting up your migration into the following steps:

    1. Update your API version and your client library
    2. If applicable, migrate code that reads from charges so that you have a consistent read path between charges created by the Charges API and the Payment Intents API
    3. Migrate your existing Charges API integration on Web, iOS, and Android to use the Payment Intents API
    4. Migrate your integration that saves cards on Customer objects
    5. Test with 3D Secure test cards to ensure your upgraded integration handles 3D Secure authentication

    Update your API version and your client library

    While the Payment Intents API works on all API versions, we recommend that you upgrade to the latest API version. If you decide to use an API version older than 2019-02-11, note the following two changes as you go through the code examples:

    • requires_source has been renamed to requires_payment_method
    • requires_source_action has been renamed to requires_action

    In addition, if you use one of our API libraries, upgrade to the latest version of the library in order to use the Payment Intents API.

    Migrating your web integration

    An integration built with Stripe.js & Elements consists of the following steps:

    1. Register your intent to collect payment on the server side
    2. Collect payment details on the client side
    3. Initiate creation of the payment
    4. Fulfill the customer’s order on the server side

    Step 1: Register intent to collect payment on the server side

    Create a PaymentIntent on your server and make it accessible on the client side.

    Before
    After

    Not possible before

    curl https://api.stripe.com/v1/payment_intents \
      -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
      -d amount=1099 \
      -d currency=usd
    
    Before
    After

    Not possible before

    Stripe::PaymentIntent.create({
      amount: 1099,
      currency: 'usd',
    })
    Before
    After

    Not possible before

    stripe.PaymentIntent.create(
      amount=1099,
      currency='usd'
    )
    Before
    After

    Not possible before

    $intent = \Stripe\PaymentIntent::create([
      'amount' => 1099,
      'currency' => 'usd',
    ]);
    Before
    After

    Not possible before

    Map<String, Object> params = new HashMap<String, Object>();
    params.put("amount", 1099);
    params.put("currency", "usd");
    
    PaymentIntent.create(params);
    Before
    After

    Not possible before

    (async () => {
      const paymentIntent = await stripe.paymentIntents.create({
        amount: 1099,
        currency: 'usd',
      });
    })();
    Before
    After

    Not possible before

    params := &stripe.PaymentIntentParams{
      Amount: stripe.Int64(1099),
      Currency: stripe.String(string(stripe.CurrencyUSD)),
    }
    intent, err = paymentintent.New(params)
    Before
    After

    Not possible before

    var service = new PaymentIntentService();
    var options = new PaymentIntentCreateOptions {
      Amount = 1099,
      Currency = "usd",
    };
    var paymentIntent = service.Create(options);

    Step 2: Collect payment details on the client side

    Use the handleCardPayment function, which collects the payment information and submits it directly to Stripe.

    Before
    After
    stripe.createToken(
      cardElement
    ).then(function(token) {
      // Send token to server
    });
    stripe.handleCardPayment(
      INTENT_SECRET_FROM_STEP_1,
      cardElement
    ).then(function(result) {
      if (result.error) {
        // Display error.message in your UI.
      } else {
        // The payment has succeeded
        // Display a success message
      }
    });
    Before
    After
    const {token} =
      await stripe.createToken(
        cardElement
      );
    // Send token to server
    const {paymentIntent, error} =
      await stripe.handleCardPayment(
        INTENT_SECRET_FROM_STEP_1,
        cardElement
      );
    if (error) {
      // Display error.message in your UI.
    } else {
      // The payment has succeeded
      // Display a success message
    }

    Step 3: Initiate creation of the payment

    In your existing integration, the final step is using tokenized payment information to create a charge on your server. This is no longer necessary, as the handleCardPayment function—called in the previous step—initiates creation of the charge.

    Before
    After
    curl https://api.stripe.com/v1/charges \
      -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
      -d source="{{FROM_PREVIOUS_STEP}}" \
      -d amount=1099 \
      -d currency=usd
    
    Completed in previous step
    Before
    After
    charge = Stripe::Charge.create(
      source: '{{FROM_PREVIOUS_STEP}}',
      amount: 1099,
      currency: 'usd',
    )
    Completed in previous step
    Before
    After
    charge = stripe.Charge.create(
      source='{{FROM_PREVIOUS_STEP}}',
      amount=1099,
      currency='usd',
    )
    
    Completed in previous step
    Before
    After
    $charge = \Stripe\Charge::create([
        'source' => '{{FROM_PREVIOUS_STEP}}',
        'amount' => 1099,
        'currency' => 'usd',
    ]);
    
    Completed in previous step
    Before
    After
    Map<String, Object> params = new HashMap<>();
    params.put("source", "{{FROM_PREVIOUS_STEP}}");
    params.put("amount", 1099);
    params.put("currency", "usd");
    Charge charge = Charge.create(params);
    
    Completed in previous step
    Before
    After
    (async () => {
      const charge = await stripe.charges.create({
        source: '{{FROM_PREVIOUS_STEP}}',
        amount: 1099,
        currency: 'usd',
      });
    })();
    
    Completed in previous step
    Before
    After
    params := &stripe.ChargeParams{
        Amount: stripe.Int64(1099),
        Currency: stripe.String(string(stripe.CurrencyUSD)),
    }
    params.SetSource("{{FROM_PREVIOUS_STEP}}")
    ch, _ := charge.New(params)
    
    Completed in previous step
    Before
    After
    var options = new ChargeCreateOptions {
        SourceId = "{{FROM_PREVIOUS_STEP}}",
        Amount = 1099,
        Currency = "usd",
    };
    var service = new ChargeService();
    Charge charge = service.Create(options);
    
    Completed in previous step

    Step 4: Fulfill the customer’s order

    With automatic confirmation, the charge is created for you asynchronously based on customer action on the client side, so you must monitor webhooks to determine when the payment completes successfully. To perform steps like order fulfillment after a customer’s payment is successful, implement support for webhooks and monitor the payment_intent.succeeded event.

    Before
    After

    If charge succeeds, fulfill.

    Subscribe to the payment_intent.succeeded webhook and fulfill in the webhook handler.

    Now that you have migrated your existing integration, use the 3D Secure test cards to verify your upgraded integration handles 3D Secure authentication.

    The Stripe.js Payment Request Button works with the Payment Intents API by using the token obtained in a traditional PaymentRequest integration and attaching it to a PaymentIntent.

    An integration built with PaymentRequest consists of the following steps:

    1. Register your intent to collect payment on the server side
    2. Collect payment details on the client side
    3. Initiate creation of the payment
    4. Fulfill the customer’s order on the server side

    Step 1: Register intent to collect payment on the server side

    Create a PaymentIntent on your server and make it accessible on the client side.

    Before
    After

    Not possible before

    curl https://api.stripe.com/v1/payment_intents \
      -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
      -d amount=1099 \
      -d currency=usd
    
    Before
    After

    Not possible before

    Stripe::PaymentIntent.create({
      amount: 1099,
      currency: 'usd',
    })
    Before
    After

    Not possible before

    stripe.PaymentIntent.create(
      amount=1099,
      currency='usd'
    )
    Before
    After

    Not possible before

    $intent = \Stripe\PaymentIntent::create([
      'amount' => 1099,
      'currency' => 'usd',
    ]);
    Before
    After

    Not possible before

    Map<String, Object> params = new HashMap<String, Object>();
    params.put("amount", 1099);
    params.put("currency", "usd");
    
    PaymentIntent.create(params);
    Before
    After

    Not possible before

    (async () => {
      const paymentIntent = await stripe.paymentIntents.create({
        amount: 1099,
        currency: 'usd',
      });
    })();
    Before
    After

    Not possible before

    params := &stripe.PaymentIntentParams{
      Amount: stripe.Int64(1099),
      Currency: stripe.String(string(stripe.CurrencyUSD)),
    }
    intent, err = paymentintent.New(params)
    Before
    After

    Not possible before

    var service = new PaymentIntentService();
    var options = new PaymentIntentCreateOptions {
      Amount = 1099,
      Currency = "usd",
    };
    var paymentIntent = service.Create(options);

    Step 2: Collect payment details on the client side

    Listen for the PaymentRequest object’s token event, which provides a token and a callback function that you can use to indicate when the payment process is complete.

    To adapt this integration to support the Payment Intents API, use the handleCardPayment function and pass it the token ID instead of sending the token to your server. When the promise returned by the handleCardPayment function resolves, invoke the completion callback.

    Before
    After
    paymentRequest.on(
      'token',
      ({token, complete}) => {
        // Send the token to the server
    
        // Complete with 'fail' and show error if error:
        complete('fail');
    
        // Complete with 'success' if success
        complete('success');
    });
    
    paymentRequest.on(
      'paymentmethod',
      ({paymentMethod, complete}) => {
        // First, confirm the PaymentIntent to
        // see if there are any payment errors.
        const {error: confirmError} =
          await stripe.confirmPaymentIntent(
            INTENT_SECRET_FROM_STEP_1
          );
    
        if (confirmError) {
          // Complete with 'fail' and show error:
          complete('fail');
        } else {
          // Otherwise, close the Payment Request
          // modal and call handleCardPayment
          // to complete any potential actions
          // required.
          complete('success');
          const {error, paymentIntent} =
            await stripe.handleCardPayment(
              INTENT_SECRET_FROM_STEP_1,
            );
    
          // Handle payment error, if any.
        }
      }
    );
    

    Step 3: Initiate creation of the payment

    In your existing integration, the final step is using tokenized payment information to create a charge on your server. This is no longer necessary, as the handleCardPayment function—called in the previous step—initiates creation of the charge.

    Before
    After
    curl https://api.stripe.com/v1/charges \
      -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
      -d source="{{FROM_PREVIOUS_STEP}}" \
      -d amount=1099 \
      -d currency=usd
    
    Completed in previous step
    Before
    After
    charge = Stripe::Charge.create(
      source: '{{FROM_PREVIOUS_STEP}}',
      amount: 1099,
      currency: 'usd',
    )
    Completed in previous step
    Before
    After
    charge = stripe.Charge.create(
      source='{{FROM_PREVIOUS_STEP}}',
      amount=1099,
      currency='usd',
    )
    
    Completed in previous step
    Before
    After
    $charge = \Stripe\Charge::create([
        'source' => '{{FROM_PREVIOUS_STEP}}',
        'amount' => 1099,
        'currency' => 'usd',
    ]);
    
    Completed in previous step
    Before
    After
    Map<String, Object> params = new HashMap<>();
    params.put("source", "{{FROM_PREVIOUS_STEP}}");
    params.put("amount", 1099);
    params.put("currency", "usd");
    Charge charge = Charge.create(params);
    
    Completed in previous step
    Before
    After
    (async () => {
      const charge = await stripe.charges.create({
        source: '{{FROM_PREVIOUS_STEP}}',
        amount: 1099,
        currency: 'usd',
      });
    })();
    
    Completed in previous step
    Before
    After
    params := &stripe.ChargeParams{
        Amount: stripe.Int64(1099),
        Currency: stripe.String(string(stripe.CurrencyUSD)),
    }
    params.SetSource("{{FROM_PREVIOUS_STEP}}")
    ch, _ := charge.New(params)
    
    Completed in previous step
    Before
    After
    var options = new ChargeCreateOptions {
        SourceId = "{{FROM_PREVIOUS_STEP}}",
        Amount = 1099,
        Currency = "usd",
    };
    var service = new ChargeService();
    Charge charge = service.Create(options);
    
    Completed in previous step

    Step 4: Fulfill the customer’s order

    With automatic confirmation, the charge is created for you asynchronously based on customer action on the client side, so you must monitor webhooks to determine when the payment completes successfully. To perform steps like order fulfillment after a customer’s payment is successful, implement support for webhooks and monitor the payment_intent.succeeded event.

    Before
    After

    If charge succeeds, fulfill.

    Subscribe to the payment_intent.succeeded webhook and fulfill in the webhook handler.

    Now that you have migrated your existing integration, use the 3D Secure test cards to verify your upgraded integration handles 3D Secure authentication.

    If you’re using the legacy version of Checkout, upgrade to the new version of Checkout—the fastest way to accept payments with Stripe. With it, you can accept one-time payments and subscriptions. You can also use Checkout without using the Stripe API on your server. Follow the Checkout migration guide to migrate your existing integration.

    If you prefer to build your own checkout flow instead, switch to Elements. Assuming you have upgraded to Elements, you can build a Payment Intents API integration using the Elements migration guide.

    If you are preparing for Strong Customer Authentication (SCA) and need to migrate to the Payment Intents API without updating how you collect payment details, follow the manual confirmation migration guide.

    In order to upgrade an existing Stripe.js v2 integration to use the Payment Intents API, first update your integration to collect payment information with Elements. Elements provides pre-built UI components and offers simple PCI compliance with SAQ A reporting.

    After you have upgraded to Elements, you can build a Payment Intents API integration using the Elements migration guide.

    If you are preparing for Strong Customer Authentication (SCA) and need to migrate to the Payment Intents API without updating how you collect payment details, follow the manual confirmation migration guide.

    Migrating your integration that saves cards on Customer objects

    A Payment Intents API integration that collects card information in the checkout flow consists of the following steps:

    1. Register your intent to collect payment on the server side
    2. Collect payment details on the client side
    3. Initiate creation of the payment
    4. Fulfill the customer’s order on the server side

    Step 1: Register intent to collect payment on the server side

    Create a PaymentIntent on your server, setting the ID of the customer to save the payment method to. Then make the PaymentIntent accessible on the client side.

    Before
    After

    Not possible before

    curl https://api.stripe.com/v1/payment_intents \
      -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
      -d customer="{{CUSTOMER_ID}}" \
      -d amount=1099 \
      -d currency=usd
    
    Before
    After

    Not possible before

    Stripe::PaymentIntent.create({
      customer: '{{CUSTOMER_ID}}',
      amount: 1099,
      currency: 'usd',
    })
    Before
    After

    Not possible before

    stripe.PaymentIntent.create(
      customer='{{CUSTOMER_ID}}',
      amount=1099,
      currency='usd'
    )
    Before
    After

    Not possible before

    $intent = \Stripe\PaymentIntent::create([
      'customer' => '{{CUSTOMER_ID}}',
      'amount' => 1099,
      'currency' => 'usd',
    ]);
    Before
    After

    Not possible before

    Map<String, Object> params = new HashMap<String, Object>();
    params.put("customer", "{{CUSTOMER_ID}}");
    params.put("amount", 1099);
    params.put("currency", "usd");
    
    PaymentIntent.create(params);
    Before
    After

    Not possible before

    (async () => {
      const paymentIntent = await stripe.paymentIntents.create({
        customer: '{{CUSTOMER_ID}}',
        amount: 1099,
        currency: 'usd',
      });
    })();
    Before
    After

    Not possible before

    params := &stripe.PaymentIntentParams{
      Amount: stripe.Int64(1099),
      Currency: stripe.String(string(stripe.CurrencyUSD)),
    }
    params.SetCustomer("{{CUSTOMER_ID}}")
    intent, err = paymentintent.New(params)
    Before
    After

    Not possible before

    var service = new PaymentIntentService();
    var options = new PaymentIntentCreateOptions {
      Amount = 1099,
      CustomerId = "{{CUSTOMER_ID}}",
      Currency = "usd",
    };
    var paymentIntent = service.Create(options);

    Step 2: Collect payment details on the client side

    Use the handleCardPayment function, which collects the payment information and submits it directly to Stripe, and save_payment_method to attach the provided payment method to the customer.

    Before
    After
    stripe.createToken(
    // or stripe.createSource
      cardElement
    ).then(function(token) {
      // Send token to server
    });
    stripe.handleCardPayment(
      '{{INTENT_SECRET_FROM_STEP_1}}',
      cardElement,
      {save_payment_method: true}
    ).then(function(result) {
      if (result.error) {
        // Display error.message in your UI.
      } else {
        // The payment has succeeded
        // Display a success message
      }
    });
    Before
    After
    const {token} =
      await stripe.createToken(
      // or stripe.createSource
        cardElement
      );
    
    const {paymentIntent, error} =
      await stripe.handleCardPayment(
        '{{INTENT_SECRET_FROM_STEP_1}}',
        cardElement,
        {save_payment_method: true},
      );
    

    Your existing integration requires a separate step to attach a payment method to the customer. This is no longer necessary, as the handleCardPayment function attaches the provided payment method to the customer.

    Before
    After
    curl https://api.stripe.com/v1/customers/{{CUSTOMER_ID}}/sources \
      -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
      -d source="{{TOKEN_OR_SOURCE}}"
    

    Completed in handleCardPayment function

    Before
    After
    card = Stripe::Customer.create_source(
      '{{CUSTOMER_ID}}',
      {
        source: '{{TOKEN_OR_SOURCE}}',
      }
    )

    Completed in handleCardPayment function

    Before
    After
    stripe.Customer.create_source(
      '{{CUSTOMER_ID}}',
      source='{{TOKEN_OR_SOURCE}}'
    )

    Completed in handleCardPayment function

    Before
    After
    $card = \Stripe\Customer::createSource(
      '{{CUSTOMER_ID}}',
      [
        'source' => '{{TOKEN_OR_SOURCE}}',
      ]
    );

    Completed in handleCardPayment function

    Before
    After
    Customer customer = Customer.retrieve("{{CUSTOMER_ID}}");
    Map<String, Object> params = new HashMap<String, Object>();
    params.put("source", "{{TOKEN_OR_SOURCE}}");
    customer.getSources().create(params);

    Completed in handleCardPayment function

    Before
    After
    (async () => {
      const card = await stripe.customers.createSource(
        '{{CUSTOMER_ID}}',
        {source: '{{TOKEN_OR_SOURCE}}'}
      );
    })();

    Completed in handleCardPayment function

    Before
    After
    params := &stripe.CardParams{
      Customer: stripe.String("{{CUSTOMER_ID}}"),
      Token: stripe.String("{{TOKEN_OR_SOURCE}}"),
    }
    c, err := card.New(params)

    Completed in handleCardPayment function

    Before
    After
    var options = new CardCreateOptions
    {
      SourceToken = "{{TOKEN_OR_SOURCE}}"
    };
    var service = new CardService();
    var card = service.Create("{{CUSTOMER_ID}}", options);

    Completed in handleCardPayment function

    Step 3: Initiate creation of the payment

    In your existing integration, the final step is using tokenized payment information to create a charge on your server. This is no longer necessary, as the handleCardPayment function—called in the previous step—initiates creation of the charge.

    Before
    After
    curl https://api.stripe.com/v1/charges \
      -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
      -d source="{{FROM_PREVIOUS_STEP}}" \
      -d customer="{{CUSTOMER_ID}}" \
      -d amount=1099 \
      -d currency=usd
    
    Completed in previous step
    Before
    After
    charge = Stripe::Charge.create(
      source: '{{FROM_PREVIOUS_STEP}}',
      customer: '{{CUSTOMER_ID}}',
      amount: 1099,
      currency: 'usd',
    )
    Completed in previous step
    Before
    After
    charge = stripe.Charge.create(
      source='{{FROM_PREVIOUS_STEP}}',
      customer='{{CUSTOMER_ID}}',
      amount=1099,
      currency='usd',
    )
    
    Completed in previous step
    Before
    After
    $charge = \Stripe\Charge::create([
        'source' => '{{FROM_PREVIOUS_STEP}}',
        'customer' => '{{CUSTOMER_ID}}',
        'amount' => 1099,
        'currency' => 'usd',
    ]);
    
    Completed in previous step
    Before
    After
    Map<String, Object> params = new HashMap<>();
    params.put("source", "{{FROM_PREVIOUS_STEP}}");
    params.put("customer", "{{CUSTOMER_ID}}");
    params.put("amount", 1099);
    params.put("currency", "usd");
    Charge charge = Charge.create(params);
    
    Completed in previous step
    Before
    After
    (async () => {
      const charge = await stripe.charges.create({
        source: '{{FROM_PREVIOUS_STEP}}',
        customer: '{{CUSTOMER_ID}}',
        amount: 1099,
        currency: 'usd',
      });
    })();
    
    Completed in previous step
    Before
    After
    params := &stripe.ChargeParams{
        Amount: stripe.Int64(1099),
        Currency: stripe.String(string(stripe.CurrencyUSD)),
    }
    params.SetSource("{{FROM_PREVIOUS_STEP}}")
    params.SetCustomer("{{CUSTOMER_ID}}")
    ch, _ := charge.New(params)
    
    Completed in previous step
    Before
    After
    var options = new ChargeCreateOptions {
        SourceId = "{{FROM_PREVIOUS_STEP}}",
        CustomerId = "{{CUSTOMER_ID}}",
        Amount = 1099,
        Currency = "usd",
    };
    var service = new ChargeService();
    Charge charge = service.Create(options);
    
    Completed in previous step

    Step 4: Fulfill the customer’s order

    With automatic confirmation, the charge is created for you asynchronously based on customer action on the client side, so you must monitor webhooks to determine when the payment completes successfully. To perform steps like order fulfillment after a customer’s payment is successful, implement support for webhooks and monitor the payment_intent.succeeded event.

    Before
    After

    If charge succeeds, fulfill.

    Subscribe to the payment_intent.succeeded webhook and fulfill in the webhook handler.

    Now that you have migrated your existing integration, use the 3D Secure test cards to verify your upgraded integration handles 3D Secure authentication.

    For saving cards outside the checkout flow, there are two differences between your existing integration and a Payment Methods API integration.

    On the client side, use the createPaymentMethod function instead of createToken or createSource.

    Before
    After
    stripe.createToken(
    // or stripe.createSource
      cardElement
    ).then(function(token) {
      // Send token to server
    });
    stripe.createPaymentMethod(
      'card',
      cardElement
    ).then(function(result) {
      if (result.error) {
        // Display error.message in your UI.
      } else {
        // The PaymentMethod has successfully been created
      }
    });
    Before
    After
    const {token} =
      await stripe.createToken(
      // or stripe.createSource
        cardElement
      );
    const {paymentMethod} =
      await stripe.createPaymentMethod(
        'card',
        cardElement
      );

    On the server side, attach the payment method to the Customer object using the Payment Methods API instead of the Customers API.

    Before
    After
    curl https://api.stripe.com/v1/customers/{{CUSTOMER_ID}}/sources \
      -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
      -d source="{{TOKEN_FROM_STEP_1}}"
    
    curl https://api.stripe.com/v1/payment_methods/{{PAYMENT_METHOD_ID}}/attach \
      -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
      -d customer="{{CUSTOMER_ID}}"
    
    Before
    After
    card = Stripe::Customer.create_source(
      '{{CUSTOMER_ID}}',
      {
        source: '{{TOKEN_FROM_STEP_1}}',
      }
    )
    Stripe::PaymentMethod.attach(
      '{{PAYMENT_METHOD_ID}}',
      {
        customer: '{{CUSTOMER_ID}}',
      }
    )
    Before
    After
    stripe.Customer.create_source(
      '{{CUSTOMER_ID}}',
      source='{{TOKEN_FROM_STEP_1}}'
    )
    payment_method = stripe.PaymentMethod.attach(
      '{{PAYMENT_METHOD_ID}}',
      customer='{{CUSTOMER_ID}}'
    )
    Before
    After
    $card = \Stripe\Customer::createSource(
      '{{CUSTOMER_ID}}',
      [
        'source' => '{{TOKEN_FROM_STEP_1}}',
      ]
    );
    $payment_method = \Stripe\PaymentMethod::retrieve('{{PAYMENT_METHOD_ID}}'))
    $payment_method->attach(['customer' => '{{CUSTOMER_ID}}']);
    Before
    After
    Customer customer = Customer.retrieve("{{CUSTOMER_ID}}");
    Map<String, Object> params = new HashMap<String, Object>();
    params.put("source", "{{TOKEN_FROM_STEP_1}}");
    customer.getSources().create(params);
    PaymentMethod paymentMethod = PaymentMethod.retrieve("{{PAYMENT_METHOD_ID}}");
    Map<String, Object> params = new HashMap<String, Object>();
    params.put("customer", "{{CUSTOMER_ID}}");
    paymentMethod.attach(params);
    Before
    After
    stripe.customers.createSource(
      '{{CUSTOMER_ID}}',
      {
        source: '{{TOKEN_FROM_STEP_1}}',
      },
      function(err, card) {
        // asynchronously called
      }
    );
    stripe.paymentMethods.attach('{{PAYMENT_METHOD_ID}}', {customer: '{{CUSTOMER_ID}}'}, function(err, paymentMethod) {
      // asynchronously called
    });
    Before
    After
    params := &stripe.CardParams{
      Customer: stripe.String("{{CUSTOMER_ID}}"),
      Token: stripe.String("{{TOKEN_FROM_STEP_1}}"),
    }
    c, err := card.New(params)
    params := &stripe.PaymentMethodAttachParams{
      Customer: "{{CUSTOMER_ID}}",
    }
    p, err := paymentmethod.Attach("{{PAYMENT_METHOD_ID}}", params)
    Before
    After
    var options = new CardCreateOptions
    {
      SourceToken = "{{TOKEN_FROM_STEP_1}}"
    };
    var service = new CardService();
    var card = service.Create("{{CUSTOMER_ID}}", options);
    var service = new PaymentMethodService();
    var options = new PaymentMethodAttachOptions
    {
      Customer = "{{CUSTOMER_ID}}",
    };
    var paymentMethod = service.Attach("{{PAYMENT_METHOD_ID}}", options);

    An integration that allows customers to select a saved payment method to pay with in your checkout flow consists of the following steps:

    1. Register your intent to collect payment on the server side
    2. Collect payment details from the customer’s saved payment method on the client side
    3. Initiate creation of the payment
    4. Fulfill the customer’s order on the server side

    Step 1: Register intent to collect payment on the server side

    When paying with a previously saved payment method, create a PaymentIntent with the ID of the Customer object.

    Before
    After

    Not possible before

    curl https://api.stripe.com/v1/payment_intents \
      -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
      -d amount=1099 \
      -d currency=usd \
      -d customer="{{CUSTOMER_ID}}"
    
    Before
    After

    Not possible before

    Stripe::PaymentIntent.create({
      amount: 1099,
      currency: 'usd',
      customer: '{{CUSTOMER_ID}}',
    })
    Before
    After

    Not possible before

    stripe.PaymentIntent.create(
      amount=1099,
      currency='usd',
      customer='{{CUSTOMER_ID}}',
    )
    Before
    After

    Not possible before

    $intent = \Stripe\PaymentIntent::create([
      'amount' => 1099,
      'currency' => 'usd',
      'customer' => '{{CUSTOMER_ID}}',
    ]);
    Before
    After

    Not possible before

    Map<String, Object> params = new HashMap<String, Object>();
    params.put("amount", 1099);
    params.put("currency", "usd");
    params.put("customer", "{{CUSTOMER_ID}}");
    
    PaymentIntent.create(params);
    Before
    After

    Not possible before

    (async () => {
      const paymentIntent = await stripe.paymentIntents.create({
        amount: 1099,
        currency: 'usd',
        customer: '{{CUSTOMER_ID}}',
      });
    })();
    Before
    After

    Not possible before

    params := &stripe.PaymentIntentParams{
      Amount: stripe.Int64(1099),
      Currency: stripe.String(string(stripe.CurrencyUSD)),
      Customer: stripe.String("{{CUSTOMER_ID}}"),
    }
    intent, err = paymentintent.New(params)
    Before
    After

    Not possible before

    var service = new PaymentIntentService();
    var options = new PaymentIntentCreateOptions {
      Amount = 1099,
      Currency = "usd",
      CustomerId = "{{CUSTOMER_ID}}",
    };
    var paymentIntent = service.Create(options);

    Step 2: Collect payment details from the customer’s saved payment method on the client side

    Use the handleCardPayment function, which collects the payment information and submits it directly to Stripe.

    Before
    After

    Send the customer’s selected saved source ID to the server.

    stripe.handleCardPayment(
      INTENT_SECRET_FROM_STEP_1,
      {payment_method: PAYMENT_METHOD_ID}
      // An existing Card or Source ID
      // can also be used
    ).then(function(result) {
      if (result.error) {
        // Display error.message in your UI.
      } else {
        // The payment has succeeded
        // Display a success message
      }
    });
    Before
    After

    Send the customer’s selected saved source ID to the server.

    const {paymentIntent, error} =
      await stripe.handleCardPayment(
        INTENT_SECRET_FROM_STEP_1,
        {payment_method: PAYMENT_METHOD_ID}
        // An existing Card or Source ID
        // can also be used
      );
    if (error) {
      // Display error.message in your UI.
    } else {
      // The payment has succeeded
      // Display a success message
    }

    Step 3: Initiate creation of the payment

    In your existing integration, the final step is using tokenized payment information to create a charge on your server. This is no longer necessary, as the handleCardPayment function—called in the previous step—initiates creation of the charge.

    Before
    After
    curl https://api.stripe.com/v1/charges \
      -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \
      -d source="{{FROM_PREVIOUS_STEP}}" \
      -d customer="{{CUSTOMER_ID}}" \
      -d amount=1099 \
      -d currency=usd
    
    Completed in previous step
    Before
    After
    charge = Stripe::Charge.create(
      source: '{{FROM_PREVIOUS_STEP}}',
      customer: '{{CUSTOMER_ID}}',
      amount: 1099,
      currency: 'usd',
    )
    Completed in previous step
    Before
    After
    charge = stripe.Charge.create(
      source='{{FROM_PREVIOUS_STEP}}',
      customer='{{CUSTOMER_ID}}',
      amount=1099,
      currency='usd',
    )
    
    Completed in previous step
    Before
    After
    $charge = \Stripe\Charge::create([
        'source' => '{{FROM_PREVIOUS_STEP}}',
        'customer' => '{{CUSTOMER_ID}}',
        'amount' => 1099,
        'currency' => 'usd',
    ]);
    
    Completed in previous step
    Before
    After
    Map<String, Object> params = new HashMap<>();
    params.put("source", "{{FROM_PREVIOUS_STEP}}");
    params.put("customer", "{{CUSTOMER_ID}}");
    params.put("amount", 1099);
    params.put("currency", "usd");
    Charge charge = Charge.create(params);
    
    Completed in previous step
    Before
    After
    (async () => {
      const charge = await stripe.charges.create({
        source: '{{FROM_PREVIOUS_STEP}}',
        customer: '{{CUSTOMER_ID}}',
        amount: 1099,
        currency: 'usd',
      });
    })();
    
    Completed in previous step
    Before
    After
    params := &stripe.ChargeParams{
        Amount: stripe.Int64(1099),
        Currency: stripe.String(string(stripe.CurrencyUSD)),
    }
    params.SetSource("{{FROM_PREVIOUS_STEP}}")
    params.SetCustomer("{{CUSTOMER_ID}}")
    ch, _ := charge.New(params)
    
    Completed in previous step
    Before
    After
    var options = new ChargeCreateOptions {
        SourceId = "{{FROM_PREVIOUS_STEP}}",
        CustomerId = "{{CUSTOMER_ID}}",
        Amount = 1099,
        Currency = "usd",
    };
    var service = new ChargeService();
    Charge charge = service.Create(options);
    
    Completed in previous step

    Step 4: Fulfill the customer’s order

    With automatic confirmation, the charge is created for you asynchronously based on customer action on the client side, so you must monitor webhooks to determine when the payment completes successfully. To perform steps like order fulfillment after a customer’s payment is successful, implement support for webhooks and monitor the payment_intent.succeeded event.

    Before
    After

    If charge succeeds, fulfill.

    Subscribe to the payment_intent.succeeded webhook and fulfill in the webhook handler.

    Now that you have migrated your existing integration, use the 3D Secure test cards to verify your upgraded integration handles 3D Secure authentication.

    Access saved payment methods

    To display the customer’s previously saved Cards, Sources, and PaymentMethods, list the payment methods instead of reading the sources property of the customer object. This is required because new PaymentMethods added to a customer will not be duplicated in the sources property of the customer object.

    Before
    After
    customer.sources
    curl "https://api.stripe.com/v1/payment_methods?customer={{CUSTOMER_ID}}&type=card" \
      -u sk_test_4eC39HqLyjWDarjtT1zdp7dc:
    
    Before
    After
    customer.sources
    Stripe::PaymentMethod.list(
      type: 'card',
      customer: '{{CUSTOMER_ID}}',
    )
    Before
    After
    customer.sources
    stripe.PaymentMethod.list(customer="{{CUSTOMER_ID}}", type="card")
    Before
    After
    customer->sources
    \Stripe\PaymentMethod::all(['customer' => '{{CUSTOMER_ID}}', 'type' => 'card']);
    Before
    After
    customer.sources
    Map<String, Object> params = new HashMap<String, Object>();
    paymentmethodParams.put("customer", "{{CUSTOMER_ID}}");
    paymentmethodParams.put("type", "card");
    
    PaymentMethod.list(params);
    
    Before
    After
    customer.sources
    const paymentMethods = await stripe.paymentMethods.list(
      { customer: "{{CUSTOMER_ID}}", type: "card" }
    );
    
    Before
    After
    customer.sources
    params := &stripe.PaymentMethodListParams{
      Customer: stripe.String("{{CUSTOMER_ID}}"),
      Type: stripe.String(string(stripe.PaymentMethodTypeCard)),
    }
    i := paymentmethod.List(params)
    for i.Next() {
      p := i.PaymentMethod()
    }
    
    
    Before
    After
    customer.Sources
    var service = new PaymentMethodService();
    var options = new PaymentMethodListOptions
    {
      Customer = "{{CUSTOMER_ID}}",
      Limit = 3,
      Type = "card"
    };
    var paymentMethods = service.List(options);
    

    Next steps

    Now you have all the information you need to begin migrating an existing Stripe integration to the Payment Intents API with automatic confirmation. To learn more, continue reading:

    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.

    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.

    On this page