# Save a card without bank authentication
Collect card details in your mobile app and charge your customer at a later time.
# React Native
Stripe allows you to collect card details and charge your customer at a later time. In some regions, banks require a second form of authentication such as entering a code sent to a phone. The extra step decreases conversion if your customer isn’t actively using your website or application because they aren’t available to authenticate the purchase.
If you primarily do business in the US and Canada, banks don’t require authentication, so you can follow this simpler integration. This integration will be non-compliant in countries that require authentication for saving cards (for example, India) so building this integration means that expanding to other countries or adding other payment methods will require significant changes. Learn how to [save cards that require authentication](https://docs.stripe.com/payments/save-and-reuse.md).
> #### Compliance
>
> You’re responsible for your compliance with all applicable laws, regulations, and network rules when saving a customer’s payment details. For instance, if you want to save their payment method for future use, such as charging them when they’re not actively using your website or app. Add terms to your website or app that state how you plan to save payment method details and allow customers to opt in. If you want to charge them when they’re offline, make sure your terms include the following:
>
> - The customer’s agreement to your initiating a payment or a series of payments on their behalf for specified transactions.
- The anticipated timing and frequency of payments (for example, if the charges are for scheduled installments, subscription payments, or unscheduled top-ups).
- How you determine the payment amount.
- Your cancellation policy, if the payment method is for a subscription service.
>
> Make sure you keep a record of your customer’s written agreement to these terms.
## Set up Stripe [Server-side] [Client-side]
### Server-side
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:
#### Ruby
```bash
# Available as a gem
sudo gem install stripe
```
```ruby
# If you use bundler, you can add this line to your Gemfile
gem 'stripe'
```
### Client-side
The [React Native SDK](https://github.com/stripe/stripe-react-native) is open source and fully documented. Internally, it uses the [native iOS](https://github.com/stripe/stripe-ios) and [Android](https://github.com/stripe/stripe-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):
#### yarn
```bash
yarn add @stripe/stripe-react-native
```
#### npm
```bash
npm install @stripe/stripe-react-native
```
Next, install some other necessary dependencies:
- For iOS, go 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.
> We recommend following the [official TypeScript guide](https://reactnative.dev/docs/typescript#adding-typescript-to-an-existing-project) to add TypeScript support.
### Stripe initialization
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](https://docs.stripe.com/keys.md#obtain-api-keys) in `publishableKey` is required. The following example shows how to initialize Stripe using the `StripeProvider` component.
```jsx
import { useState, useEffect } from 'react';
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 (
{/* Your app code here */}
);
}
```
> Use your API [test keys](https://docs.stripe.com/keys.md#obtain-api-keys) while you test and develop, and your [live mode](https://docs.stripe.com/keys.md#test-live-modes) keys when you publish your app.
## Collect card details [Client-side]
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.

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.
```javascript
import { CardField, useStripe } from '@stripe/stripe-react-native';
function PaymentScreen() {
// ...
return (
{
console.log('cardDetails', cardDetails);
}}
onFocus={(focusedField) => {
console.log('focusField', focusedField);
}}
/>
);
}
```
Run your app and make sure your checkout page shows the `CardField` component. When the customer taps **Pay**, use `createPaymentMethod` to collect the card details and create a *PaymentMethod* (PaymentMethods represent your customer's payment instruments, used with the Payment Intents or Setup Intents APIs). Send the ID of the PaymentMethod to your server.
```javascript
const pay = () => {
const { paymentMethod, error } = await createPaymentMethod({
paymentMethodType: 'Card',
paymentMethodData: {
billingDetails: {
name: 'Jenny Rosen',
}
},
});
if (error) {
// Handle error
} else if (paymentMethod) {
const paymentMethodId = paymentMethod.id;
// Send the ID of the PaymentMethod to your server for the next step
// ...
}
};
```
## Save the card [Server-side]
Save the card by attaching the PaymentMethod to a *Customer* (Customer objects represent customers of your business. They let you reuse payment methods and give you the ability to track multiple payments). You can use the Customer object to store other information about your customer, such as shipping details and email address.
```curl
curl https://api.stripe.com/v1/customers \
-u "<>:" \
-d payment_method={{PAYMENT_METHOD_ID}}
```
If you have an existing Customer, you can attach the PaymentMethod to that object instead.
```curl
curl https://api.stripe.com/v1/payment_methods/{{PAYMENT_METHOD_ID}}/attach \
-u "<>:" \
-d "customer={{CUSTOMER_ID}}"
```
At this point, associate the ID of the Customer object and the ID of the PaymentMethod with your own internal representation of a customer, if you have one.
## Charge the saved card [Server-side]
When you’re ready to charge the Customer, look up the PaymentMethod ID to charge. You can do this by either storing the IDs of both in your database, or by using the Customer ID to look up all the Customer’s available PaymentMethods.
#### Accounts v2
```curl
curl -G https://api.stripe.com/v1/payment_methods \
-u "<>:" \
-d "customer_account={{CUSTOMERACCOUNT_ID}}" \
-d type=card
```
#### Customers v1
```curl
curl -G https://api.stripe.com/v1/payment_methods \
-u "<>:" \
-d "customer={{CUSTOMER_ID}}" \
-d type=card
```
Use the PaymentMethod ID and the Customer ID to create a new PaymentIntent. Set [error_on_requires_action](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-error_on_requires_action) to true to decline payments that require any actions from your customer, such as two-factor authentication.
```curl
curl https://api.stripe.com/v1/payment_intents \
-u "<>:" \
-d amount=1099 \
-d currency=usd \
-d "payment_method_types[]=card" \
-d "customer={{CUSTOMER_ID}}" \
-d payment_method={{PAYMENT_METHOD_ID}} \
-d error_on_requires_action=true \
-d confirm=true
```
When a payment attempt fails, the request also fails with a 402 HTTP status code and Stripe throws an error. You need to notify your customer to return to your application (for example, by sending an in-app notification) to complete the payment. Check the code of the [Error](https://docs.stripe.com/api/errors/handling.md) raised by the Stripe API library or check the [last_payment_error.decline_code](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-last_payment_error-decline_code) on the PaymentIntent to inspect why the card issuer declined the payment.
## Handle any card errors
Notify your customer that the payment failed and direct them to the payment form you made in step 1 where they can enter new card details. Send that new PaymentMethod ID to your server to [attach](https://docs.stripe.com/api/payment_methods/attach.md) to the Customer object and make the payment again.
Alternatively, you can create a PaymentIntent and save a card in one API call if you already created a Customer.
```curl
curl https://api.stripe.com/v1/payment_intents \
-u "<>:" \
-d amount=1099 \
-d currency=usd \
-d "payment_method_types[]=card" \
-d "customer={{CUSTOMER_ID}}" \
-d payment_method={{PAYMENT_METHOD_ID}} \
-d error_on_requires_action=true \
-d confirm=true \
-d setup_future_usage=on_session
```
Setting [setup_future_usage](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-setup_future_usage) to `on_session` indicates to Stripe that you want to save the card for later, without triggering unnecessary authentication.
## Test the integration
Stripe provides [test cards](https://docs.stripe.com/testing.md) you can use in a sandbox to simulate the behavior of different cards. Use these cards with any CVC, postal code, and expiration date in the future.
| Number | Description |
| ---------------- | ----------------------------------------------------------------------------------------------------- |
| 4242424242424242 | Succeeds and immediately processes the payment. |
| 4000000000009995 | Always fails with a decline code of `insufficient_funds`. |
| 4000002500003155 | Requires authentication, which in this integration declines with a code of `authentication_required`. |
## Optional: Re-collect a CVC
When creating subsequent payments on a saved card, you might want to re-collect the CVC of the card as an additional fraud measure to verify the user.
Start by creating a PaymentIntent on your server with the amount, currency, and your [Customer](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-customer) ID. [List](https://docs.stripe.com/api/payment_methods/list.md) the *PaymentMethods* (PaymentMethods represent your customer's payment instruments, used with the Payment Intents or Setup Intents APIs) associated with your *Customer* (Customer objects represent customers of your business. They let you reuse payment methods and give you the ability to track multiple payments) to determine which PaymentMethods to show for CVC re-collection. After re-collecting the customer’s CVC information, call `confirmPayment` method with the customer’s `CVC` and `paymentMethodId`.
```javascript
const pay = async (cvc: string) => {
const {
clientSecret,
paymentMethodId,
} = await fetchPaymentIntentClientSecret();
const {error, paymentIntent} = await confirmPayment(clientSecret, {
paymentMethodType: 'Card',
paymentMethodData: {
cvc,
paymentMethodId,
},
});
if (error) {
Alert.alert(`Error code: ${error.code}`, error.message);
} else if (paymentIntent.status === PaymentIntent.Status.Succeeded) {
Alert.alert('Success', 'The payment was confirmed successfully!');
} else {
// Handle other statuses accordingly
}
};
```
A payment might succeed even with a failed CVC check. To prevent this, configure your [Radar rules](https://docs.stripe.com/radar/rules.md#traditional-bank-checks) to block payments when CVC verification fails.
## Upgrade your integration to handle card authentication
This integration **declines cards that require authentication during payment**. If you start seeing many payments in the Dashboard listed as `Failed`, then it’s time to [upgrade your integration](https://docs.stripe.com/payments/payment-intents/upgrade-to-handle-actions.md). Stripe’s global integration handles these payments instead of automatically declining.