Set up future card payments
Use manual server-side confirmation or present payment methods separately.
Warning
We recommend that you follow the Set up future payments guide. Only use this guide if you need to use manual server-side confirmation or your integration requires presenting payment methods separately. If you’ve already integrated with Elements, see the Payment Element migration guide.
The Setup Intents API lets you save a customer’s card without an initial payment. This is helpful if you want to onboard customers now, set them up for payments, and charge them in the future—when they’re offline.
Use this integration to set up recurring payments or to create one-time payments with a final amount determined later, often after the customer receives your service.
Set up StripeServer-sideClient-side![](https://b.stripecdn.com/docs-statics-srv/assets/fcc3a1c24df6fcffface6110ca4963de.svg)
Server-side ![](https://b.stripecdn.com/docs-statics-srv/assets/fcc3a1c24df6fcffface6110ca4963de.svg)
This integration requires endpoints on your server that talk to the Stripe API. Use our official libraries for access to the Stripe API from your server:
Client-side ![](https://b.stripecdn.com/docs-statics-srv/assets/fcc3a1c24df6fcffface6110ca4963de.svg)
The React Native SDK is open source and fully documented. Internally, it uses the native iOS and Android SDKs. To install Stripe’s React Native SDK, run one of the following commands in your project’s directory (depending on which package manager you use):
Next, install some other necessary dependencies:
- For iOS, navigate to the ios directory and run
pod install
to ensure that you also install the required native dependencies. - For Android, there are no more dependencies to install.
Stripe initialization![](https://b.stripecdn.com/docs-statics-srv/assets/fcc3a1c24df6fcffface6110ca4963de.svg)
To initialize Stripe in your React Native app, either wrap your payment screen with the StripeProvider
component, or use the initStripe
initialization method. Only the API publishable key in publishableKey
is required. The following example shows how to initialize Stripe using the StripeProvider
component.
import { StripeProvider } from '@stripe/stripe-react-native'; function App() { const [publishableKey, setPublishableKey] = useState(''); const fetchPublishableKey = async () => { const key = await fetchKey(); // fetch key from your server here setPublishableKey(key); }; useEffect(() => { fetchPublishableKey(); }, []); return ( <StripeProvider publishableKey={publishableKey} merchantIdentifier="merchant.identifier" // required for Apple Pay urlScheme="your-url-scheme" // required for 3D Secure and bank redirects > // Your app code here </StripeProvider> ); }
Create a Customer before setupServer-side![](https://b.stripecdn.com/docs-statics-srv/assets/fcc3a1c24df6fcffface6110ca4963de.svg)
To set a card up for future payments, you must attach it to a Customer. Create a Customer object when your customer creates an account with your business. Customer objects allow for reusing payment methods and tracking across multiple payments.
Successful creation returns the Customer object. You can inspect the object for the customer id
and store the value in your database for later retrieval.
You can find these customers in the Customers page in the Dashboard.
Create a SetupIntentServer-side![](https://b.stripecdn.com/docs-statics-srv/assets/fcc3a1c24df6fcffface6110ca4963de.svg)
A SetupIntent is an object that represents your intent to set up a payment method for future payments. The SetupIntent object contains a client secret, a unique key that you pass to your app.
The client secret lets you perform certain actions on the client, such as confirming the setup and updating payment method details, while hiding sensitive information like customer
. You can use the client secret to validate and authenticate card details using the credit card networks. The client secret is sensitive—don’t log it, embed it in URLs, or expose it to anyone but the customer.
Server-side![](https://b.stripecdn.com/docs-statics-srv/assets/fcc3a1c24df6fcffface6110ca4963de.svg)
On your server, make an endpoint that creates a SetupIntent and returns its client secret to your app.
If you only plan on using the card for future payments when your customer is present during the checkout flow, set the usage parameter to on_session to improve authorization rates.
Collect card detailsClient-side![](https://b.stripecdn.com/docs-statics-srv/assets/fcc3a1c24df6fcffface6110ca4963de.svg)
Securely collect card information on the client with CardField
, a UI component provided by the SDK that collects the card number, expiration date, CVC, and postal code.
CardField
performs real-time validation and formatting.
Add the CardField
component to your payment screen to securely collect card details from your customers. Use the onCardChange
callback to inspect non-sensitive information about the card, like the brand, and whether the details are complete.
import { CardField, useStripe } from '@stripe/stripe-react-native'; function PaymentScreen() { // ... return ( <View> <CardField postalCodeEnabled={true} placeholders={{ number: '4242 4242 4242 4242', }} cardStyle={{ backgroundColor: '#FFFFFF', textColor: '#000000', }} style={{ width: '100%', height: 50, marginVertical: 30, }} onCardChange={(cardDetails) => { console.log('cardDetails', cardDetails); }} onFocus={(focusedField) => { console.log('focusField', focusedField); }} /> </View> ); }
Caution
When saving card details to use for future off-session payments, especially in Europe because of regulations around card reuse, get permission to save a card. Include text in your checkout flow to inform your customer how you intend to use the card.
To complete the setup, pass the customer card and billing information to confirmSetupIntent
. You can access this method using either the useConfirmSetupIntent
or useStripe
hook.
function PaymentScreen() { // ... const { confirmSetupIntent, loading } = useConfirmSetupIntent(); // ... const handlePayPress = async () => { // Gather the customer's billing information (for example, email) const billingDetails: BillingDetails = { email: 'jenny.rosen@example.com', }; // Create a setup intent on the backend const clientSecret = await createSetupIntentOnBackend(); const { setupIntent, error } = await confirmSetupIntent(clientSecret, { paymentMethodType: 'Card', paymentMethodData: { billingDetails, } }); if (error) { //Handle the error } // ... }; return ( <View> // ... <Button onPress={handlePayPress} title="Save" loading={loading} /> </View> ); }
Some payment methods require additional authentication steps to complete a payment. The SDK manages the payment confirmation and authentication flow, which might involve presenting additional screens required for authentication. To test the authentication process, use the test card 4000 0025 0000 3155
along with any CVC, postal code, and future expiration date.
When the SetupIntent
succeeds, the resulting PaymentMethod ID (in setupIntent.
) is saved to the provided Customer
.
Charge the saved card laterServer-side![](https://b.stripecdn.com/docs-statics-srv/assets/fcc3a1c24df6fcffface6110ca4963de.svg)
When you are ready to charge your customer off-session, use the Customer and PaymentMethod IDs to create a PaymentIntent. To find a card to charge, list the PaymentMethods associated with your Customer.
When you have the Customer and PaymentMethod IDs, create a PaymentIntent with the amount and currency of the payment. Set a few other parameters to make the off-session payment:
- Set off_session to
true
to indicate that the customer is not in your checkout flow during this payment attempt. This causes the PaymentIntent to throw an error if authentication is required. - Set the value of the PaymentIntent’s confirm property to
true
, which causes confirmation to occur immediately when the PaymentIntent is created. - Set payment_method to the ID of the PaymentMethod and customer to the ID of the Customer.
Start a recovery flow![](https://b.stripecdn.com/docs-statics-srv/assets/fcc3a1c24df6fcffface6110ca4963de.svg)
If the PaymentIntent has any other status, the payment didn’t succeed and the request fails. Notify your customer to return to your application (for example, by email, text, push notification) to complete the payment. We recommend creating a recovery flow in your app that shows why the payment failed initially and lets your customer retry it.
In your recovery flow, retrieve the PaymentIntent using its client secret. Check the PaymentIntent’s lastPaymentError
to inspect why the payment attempt failed. For card errors, you can show the user the last message for the payment error. Otherwise, you can show a generic failure message.
function PaymentScreen() { // ... const {retrievePaymentIntent} = useStripe(); // ... const handleRecoveryFlow = async () => { const {paymentIntent, error} = await retrievePaymentIntent(clientSecret); if (error) { Alert.alert(`Error: ${error.code}`, error.message); } else if (paymentIntent) { // Default to a generic error message const failureReason = 'Payment failed, try again.'; if (paymentIntent.lastPaymentError.type === 'Card') { failureReason = paymentIntent.lastPaymentError.message; } } }; return ( <View> // ... <Button onPress={handleRecoveryFlow} title="Recovery flow" loading={loading} /> </View> ); }
Let your customer try again![](https://b.stripecdn.com/docs-statics-srv/assets/fcc3a1c24df6fcffface6110ca4963de.svg)
Give the customer the option to update or remove their saved card and try payment again in your recovery flow. Follow the same steps you did to accept their initial payment with one difference—confirm the original, failed PaymentIntent by reusing its client secret instead of creating a new one.
If the payment failed because it requires authentication, try again with the existing PaymentMethod instead of creating a new one.
function PaymentScreen() { // ... const {retrievePaymentIntent} = useStripe(); // ... const handleRecoveryFlow = async () => { const {paymentIntent, error} = await retrievePaymentIntent(clientSecret); if (error) { Alert.alert(`Error: ${error.code}`, error.message); } else if (paymentIntent) { // Default to a generic error message let failureReason = 'Payment failed, try again.'; if (paymentIntent.lastPaymentError.type === 'Card') { failureReason = paymentIntent.lastPaymentError.message; } // If the last payment error is authentication_required, let the customer // complete the payment without asking them to reenter their details. if (paymentIntent.lastPaymentError?.code === 'authentication_required') { // Let the customer complete the payment with the existing PaymentMethod const {error} = await confirmPayment(paymentIntent.clientSecret, { paymentMethodType: 'Card', paymentMethodData: { billingDetails, paymentMethodId: paymentIntent.lastPaymentError?.paymentMethod.id, }, }); if (error) { // handle error } } else { // Collect a new PaymentMethod from the customer } } }; return ( <View> // ... <Button onPress={handleRecoveryFlow} title="Recovery flow" loading={loading} /> </View> ); }
Test the integration![](https://b.stripecdn.com/docs-statics-srv/assets/fcc3a1c24df6fcffface6110ca4963de.svg)
By this point you should have an integration that:
- Collects and saves card details without charging the customer by using a SetupIntent
- Charges the card off-session and has a recovery flow to handle declines and authentication requests
There are several test cards you can use to make sure this integration is ready for production. Use them with any CVC, postal code, and future expiration date.
Number | Description |
---|---|
Succeeds and immediately processes the payment. | |
Requires authentication for the initial purchase, but succeeds for subsequent payments (including off-session ones) as long as the card is setup with setup_ . | |
Requires authentication for the initial purchase, and fails for subsequent payments (including off-session ones) with an authentication_ decline code. | |
Requires authentication for the initial purchase, but fails for subsequent payments (including off-session ones) with an insufficient_ decline code. | |
Always fails (including the initial purchase) with a decline code of insufficient_ . |
See the full list of test cards.