Learn how to migrate your existing cards and Charges API integration.
Use this migration path if your integration waits for the response from the client before finalizing the payment on the server. Server-side confirmation has the following limitations:
Only supports cards: You’ll have to write more code to support ACH and popular regional payment methods separately.
Double-charge risk: When you create a new PaymentIntent each time your customer attempts to pay, you risk accidentally double-charging your customer. Be sure to follow best practices.
Extra trip to client: If your customer has a card with 3D Secure or is subject to regulations like Strong Customer Authentication, this integration requires an extra trip to the client to authenticate.
If you don’t mind these limitations, feel free to use this migration path. Otherwise, use the standard migration path, which automatically handles any actions required for collecting payment.
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, you can split up the migration into the following steps:
If applicable, migrate code that reads from Charge properties so that you have a consistent read path between charges created by the Charges API and charges created by the Payment Intents API. This ensures a read-side integration that works for both your old and new payments integrations.
Migrate your existing Charges API integration on Web, iOS, and Android to use the Payment Intents API.
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 Client libraries, upgrade to the latest version of the library in order to use the Payment Intents API.
Migrate your one-time payment flows
“One-time payments” are payment flows that collect card details to be charged once and immediately (for example, e-commerce purchases, one-off donations). For payment flows that save card details to be charged later or on a recurring basis, follow the migration guide on saving cards.
Pick the migration path that matches your current Stripe integration:
An integration built with Stripe.js & Elements consists of the following steps:
Collect payment details on the client side
Attempt the payment on the server side
Handle the server response on the client side
Step 1: Collect payment details on the client side
Use the createPaymentMethod function, which collects the payment information and submits it directly to Stripe.
Before
After
stripe.createToken(// or stripe.createSource
cardElement
).then(function(result){if(result.error){// Show error in payment form}else{// Send token.id to serverfetch('/ajax/confirm_payment',{
method:'POST',
headers:{'Content-Type':'application/json'},
body:JSON.stringify({
token_id: result.token.id})}).then(function(result){// Handle server response (see Step 3)
result.json().then(function(json){handleServerResponse(json);})});}});
stripe.createPaymentMethod({
type:'card',
card: cardElement
}).then(function(result){if(result.error){// Show error in payment form}else{// Send paymentMethod.id to serverfetch('/ajax/confirm_payment',{
method:'POST',
headers:{'Content-Type':'application/json'},
body:JSON.stringify({
payment_method_id: result.paymentMethod.id})}).then(function(result){// Handle server response (see Step 3)
result.json().then(function(json){handleServerResponse(json);})});}});
Step 2: Attempt the payment on the server side
In your existing integration, the final step is using tokenized payment information to create a charge on your server. With the Payment Intents API, this is sufficient if additional steps like 3D Secure authentication are not required. However, if some payments may require additional authentication, then additional code is required. Instead of creating a Charge, we’ll create a PaymentIntent.
Check the PaymentIntent status with generate_payment_response. If the payment requires additional actions such as 3D Secure authentication, the PaymentIntent’s status will be set to requires_action. If the payment is successful, then the status is set to succeeded and the payment is complete. If the payment failed, the status is set to requires_payment_method.
# If status == 'requires_action' and# next_action.type == 'use_stripe_sdk'# -> Tell the client to handle the action## If status == 'succeeded'# -> The payment didn't need any additional actions and completed!# -> Handle post-payment fulfillment
Step 3: Handle the server response on the client side
On the client side, handle the new case when additional action is required with stripe.handleCardAction. After it completes without error, the PaymentIntent now has a status of requires_confirmation. Send the ID of the PaymentIntent to the same server endpoint to confirm the payment again.
Before
After
functionhandleServerResponse(response){if(response.error){// Show error on payment form}else{// Show success message}}
functionhandleServerResponse(response){if(response.error){// Show error from server on payment form}elseif(response.requires_action){// Use Stripe.js to handle required card actionhandleAction(response);}else{// Show success message}}functionhandleAction(response){
stripe.handleCardAction(
response.payment_intent_client_secret).then(function(result){if(result.error){// Show error in payment form}else{// The card action has been handled// The PaymentIntent can be confirmed again on the serverfetch('/ajax/confirm_payment',{
method:'POST',
headers:{'Content-Type':'application/json'},
body:JSON.stringify({
payment_intent_id: result.paymentIntent.id})}).then(function(confirmResult){return confirmResult.json();}).then(handleServerResponse);}});}
Now that you have migrated, use the test cards in the following section to verify your upgraded integration handles 3D Secure authentication.
Migrate your integration that saves cards on Customer objects
You will need to change your integration to let Stripe know how you intend to use your customers’ saved cards so we can optimize for the right exemptions. SCA may also require that the card is authenticated before being stored.
For saving cards in the checkout flow after a payment is made, there are a few differences between your existing integration and a Payment Intents API integration with manual confirmation.
stripe.createToken(// or stripe.createSource
cardElement
).then(function(result){if(result.error){// Display result.error.message in UI}else{// Send result.token to server}});
stripe.createPaymentMethod({
type:'card',
card: cardElement,}).then(function(result){if(result.error){// Display result.error.message in UI}else{// The PaymentMethod has successfully// been created}});
On the server side, use the setup_future_usage parameter on the PaymentIntent to save the card for future use. You can pass a Customer ID to the PaymentIntent to save the payment method immediately upon creation, or attach the payment method to a Customer at a later time.
setup_future_usage tells Stripe how you intend to use the saved card and allows for optimizing the authentication process for future payments. Learn more about setup_future_usage in the Payment Intents API manual.
If the card requires authentication before it can be charged and saved to a customer, it will have a status of requires_action. If the PaymentIntent requires action, call handleCardAction on the client with the PaymentIntent’s client_secret. handleCardAction handles displaying the UI to the customer to authenticate the card. Once authentication is complete, you can confirm the PaymentIntent again on the server to finalize the payment. Learn more about handling require card actions in the one-time payments guide.
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.
It’s important to thoroughly test your integration to make sure you’re correctly handling cards that require additional authentication and cards that don’t. Use these card numbers in test mode with any expiration date in the future and any three digit CVC code to validate your integration when authentication is required and when it’s not required.
Number
Authentication
Description
Required on setup or first transaction
This test card requires authentication for one-time payments. However, if you set up this card using the Setup Intents API and use the saved card for subsequent payments, no further authentication is needed.
Required
This test card requires authentication on all transactions.
Required
This test card requires authentication, but payments will be declined with an insufficient_funds failure code after successful authentication.
Supported
This test card supports authentication via 3D Secure 2, but does not require it. Payments using this card do not require additional authentication in test mode unless your test mode Radar rules request authentication.
Use these cards in your application or the payments demo to see the different behavior.
Next steps
Now you have all the information you need to begin migrating an existing Stripe integration to the Payment Intents API with manual confirmation. To learn more, continue reading:
Welcome to the Stripe Shell!
Stripe Shell is a browser-based shell with the Stripe CLI pre-installed. Login to Stripe
docs and press Control + Backtick on your keyboard to start managing your Stripe resources
in test mode.
- View supported commands:
- Find webhook events:
- Listen for webhook events:
- Call Stripe APIs: stripe [api resource] [operation] (e.g. )