Stripe React Native Migration Function by Function - Tipsi Migration Guide, Part 3

Authors
Jakub Kłobus
Software Engineer
@
Callstack
Arek Kubaczkowski
Software Engineer
@
Callstack

If you have ever integrated payments with React Native, you may have encountered the popular community library “tipsi-stripe“. This project has been unmaintained for several years and the README now suggests migrating to the official Stripe React Native project.

To better support developers looking to migrate from “tipsi-stripe” to stripe-react-native, we’ve prepared The Stripe React Native Migration Guide to walk you through the core concepts and integration steps.

This article is the first part of the guide in which we go through the process of migrating from tipsi-stripe to stripe-react-native.

The guide is divided into five parts in which you'll learn about the following aspects of the migration process:

Introduction

This article is part 3 of our ongoing series on migrating from Tipsi-Stripe to stripe-react-native. Make sure to read the previous articles on understanding stripe react native and configuring the stripe-react-native library.

Are you migrating your app’s payments logic from tipsi-stripe to stripe-react-native? In this article, we cover how to migrate each payment type and function you might use. As you could see in the previous posts, the initial setup of stripe-react-native is simple and intuitive.  Today, we will focus on each payment function in tipsi-stripe and describe how to migrate it to stripe-react-native.

Card components

One of the essential features of using Stripe is collecting the card details in order to process a payment. Tipsi-stripe has a PaymentCardTextField component and onParamsChange prop that is used to collect card details. Collected card details have to be stored in a state in order to use them in card payment.

With stripe-react-native there is no need to collect card data manually. The library does that for you. It makes it both easy to use and much more secure. Just render CardField every time you want to use card-related payment.

Before (tipsi-stripe):

const [cardParams, setCardParams] = useState();
// ...
return (
<PaymentCardTextField
style={styles.field}
cursorColor={...}
textErrorColor={...}
placeholderColor={...}
numberPlaceholder={...}
expirationPlaceholder={...}
cvcPlaceholder={...}
disabled={false}
onParamsChange={(valid, params) => {
// params contains number, expMonth, expYear and cvc
setCardParams(params);
}}
/>
);
view raw gistfile1.ts hosted with ❤ by GitHub

After (stripe-react-native):

<CardField
postalCodeEnabled={false}
autofocus
placeholder={{
number: '4242 4242 4242 4242',
postalCode: '12345',
cvc: 'CVC',
expiration: 'MM|YY',
}}
onCardChange={(cardDetails) => {
console.log('cardDetails', cardDetails);
}}
onFocus={(focusedField) => {
console.log('focusField', focusedField);
}}
cardStyle={{
borderWidth: 1,
backgroundColor: '#FFFFFF',
borderColor: '#000000',
borderRadius: 8,
fontSize: 14,
placeholderColor: '#999999',
}}
style={{ width: 100%, height: 200 }}
/>
view raw gistfile1.ts hosted with ❤ by GitHub

In the above code examples, the placeholder information is now a single object called placeholder. In addition, basic component styles can be modified using single cardStyle prop instead of multiple ones (cursorColor, placeholderColor, etc.).

Another difference with the CardField component is the set of methods on its ref. Tipsi has blur, focus, isFocused and iOS-only setParams. First two are exactly the same in stripe-react-native. However, in order to know if card input is focused, you should use the onFocus prop and handle this information declaratively instead of calling an imperative isFocused function. Setting params are not available in stripe-react-native but we believe that since this is not supported by both platforms there is no need for that in React Native applications. This decision together with onCardChange prop which sends only the last four digits instead of the whole card number improves the security of the CardField component.

NOTE: If your app requires access to the entire card number it is still possible with stripe-react-native. You can set dangerouslyGetFullCardDetails to true in order to have a full card number returned in the onCardChange handler. Keep in mind this is not recommended since your customers' information must be protected at all costs.

Accepting a payment

With PaymentIntent API there are some significant differences between libraries. The biggest one is that in stripe-react-native for PCI compliance reasons all of the sensitive data is sent to the specific methods under the hood and you don’t need to hand it over on your own. This means that in order to proceed with a Card payment, you have to collect the data using either CardField or CardForm component provided by stripe-react-native library. In this way, the personal data is secure, as it is kept confidential from developers.

Create a payment method

In both libraries for creating a payment method you can use createPaymentMethod function. If it comes to card payments main difference was described above - instead of passing card details now you just have to set the type param to "Card" and be sure that CardField component is rendered on your screen.

Before (tipsi-stripe):

try {
const paymentMethod = await stripe.createPaymentMethod({
card: {
number: '4000002500003155',
cvc: '123',
expMonth: 11,
expYear: 2020,
},
});
} catch (e) {
// Handle error
}
view raw gistfile1.ts hosted with ❤ by GitHub

After (stripe-react-native):

// ...
const { paymentMethod, error } = await createPaymentMethod({
type: 'Card',
billingDetails: {
email: 'email@stripe.com',
}, // optional
});
// ...
return (
<CardField style={{ width: '100%', height: 100 }} postalCodeEnabled={true} />
);
view raw gistfile1.ts hosted with ❤ by GitHub

In both libraries provide billing details using the billingDetails field which contains address, email, name, and phone. See the api reference for more details.

NOTE: If you are using token to provide card details to createPaymenMethod you can still do this. The only difference is the way of generating it. More details you will find later in this article.

Confirm payment intent (for manual payment flows)

If your payment requires confirmation, you have to call a specific function in order to launch an activity where the user can authenticate the payment. For manual payment flow, there is an authenticatePaymentIntent method in tipsi-stripe and the equivalent in stripe-react-native is handleCardAction.

Before (tipsi-stripe):

await stripe.authenticatePaymentIntent({ clientSecret: 'client_secret' });
view raw gistfile1.ts hosted with ❤ by GitHub

After (stripe-react-native):

const { error, paymentIntent } = await handleCardAction('client_secret');
view raw gistfile1.ts hosted with ❤ by GitHub

In the above example, you can see that clientSecret is now a separate and the first argument of the function. This new implementation, just as in Tipsi, returns a Promise with payment intent details. In Tipsi, you could access there intent id and its status, stripe-react-native returns an error and the whole PaymentIntent object.

NOTE: It is worth mentioning that in error scenarios stripe-react-native always resolves promise and returns the error instead of rejecting it.

Confirm payment intent (for automatic payment flows)

For automatic confirmation flows, you just need to change the way you use confirmPaymentIntent method. In stripe-react-native function name was changed to confirmPayment but the main difference is that instead of passing client secret and payment details in a single object you need to pass them as two separate function arguments.

As with other payment methods, now there is no need to pass card details manually. You just need to specify the payment type and everything else will be collected automatically.

Before (tipsi-stripe):

const confirmPaymentResult = await stripe.confirmPaymentIntent({
clientSecret: 'client_secret',
paymentMethod: {
billingDetails: {
//...
},
card: {
cvc: '242',
expMonth: 11,
expYear: 2040,
number: '4000002500003155',
}
}
})
view raw gistfile1.ts hosted with ❤ by GitHub

After (stripe-react-native):

// ...
const { error, paymentIntent } = await confirmPayment('client_secret', {
type: 'Card',
billingDetails: {
// ...
},
});
// ...
return (
<CardField
// ...
/>
)
view raw gistfile1.ts hosted with ❤ by GitHub

See confirmPayment for more details about possible configuration and different payment types.

Future payments

In tipsi-stripe to set up recurring payments or to create one-time payments with a final amount determined later, there is a confirmSetupIntent method. stripe-react-native has the same function but with a slightly changed API. The differences here are almost the same as with confirmPaymentIntent function. Now you pass client secret as a first function argument and there is no need for card details.

Before (tipsi-stripe):

try {
const result = await stripe.confirmSetupIntent({
clientSecret: 'client_secret',
paymentMethod: {
billingDetails: {
//...
},
card: {
cvc: '242',
expMonth: 11,
expYear: 2040,
number: '4000002500003155',
}
}
});
} catch (e) {
// handle exception here
}
view raw gistfile1.ts hosted with ❤ by GitHub

After (stripe-react-native):

const { error, setupIntent } = await confirmSetupIntent('client_secret', {
type: 'Card',
billingDetails: {
// ...
}
});
// ...
return (
<CardField style={{ width: '100%', height: 100 }} postalCodeEnabled={true} />
);
view raw gistfile1.ts hosted with ❤ by GitHub

Create a token

Stripe provides a way to convert information collected by card components into a single-use token that you safely pass to your server to use in an API call.

Tipsi has a createTokenWithCard function for that. It differs from the new createToken only in the way how to pass card data and the fact that it doesn't require to manually pass card details.

Before (tipsi-stripe):

const params = {
number: '4242424242424242',
expMonth: 11,
expYear: 17,
cvc: '223',
name: 'Test User',
currency: 'usd',
addressLine1: '123 Test Street',
addressLine2: 'Apt. 5',
addressCity: 'Test City',
addressState: 'Test State',
addressCountry: 'Test Country',
addressZip: '55555',
};
const token = await stripe.createTokenWithCard(params);
view raw gistfile1.ts hosted with ❤ by GitHub

After (stripe-react-native):

const { createToken } = useStripe();
// ...
const { token, error } = createToken({
type: 'Card'
address: {
country: 'US',
// ...
},
name: 'card_name'
});
// ...
return (
<CardField
style={{ width: '100%', height: 100 }}
/>
);
view raw gistfile1.ts hosted with ❤ by GitHub

Remember that as with other card related functions, to use createToken function you have to render CardField component .

NOTE: tipsi-stripe provides also a way to create a bank account token using createTokenWithBankAccount function. This is not yet supported by stripe-react-native but should be ready the upcoming release. This functionality will be added as a part of createToken function.

ApplePay

deviceSupportsNativePay

Before displaying Apple Pay as a payment option in your app, first determine if the user’s device supports Apple Pay and that they have a card added to their wallet. In order to this stripe-react-native provides isApplePaySupported boolean value from useApplePay hook.

Before (tipsi-stripe):

import stripe from 'tipsi-stripe'
// ...
const [isApplePaySupported, setIsApplePaySupported] = useState();
useEffect(() => {
const checkIsSupported = async () => {
const isSupported = await stripe.deviceSupportsNativePay();
setIsApplePaySupported(isSupported);
};
checkIsSupported();
}, [setIsApplePaySupported]);
// ...
return (
// ...
{isApplePaySupported && (
<Button onPress={pay} />
)}
)
view raw gistfile1.ts hosted with ❤ by GitHub

After (stripe-react-native):

import { useApplePay } from '@stripe/stripe-react-native'
// ...
const { isApplePaySupported } = useApplePay()
// ...
return (
// ...
{isApplePaySupported && (
<Button onPress={pay} />
)}
)
view raw gistfile1.ts hosted with ❤ by GitHub

NOTE: If your application doesn't use functional components, as an alternative you can import isApplePaySupported method directly from the library.

paymentRequestWithNativePay

Requesting payment in Tipsi can be done with paymentRequestWithNativePay function. It takes a list of items and an options object with shipping details and other properties. After migration, you should use presentApplePay function and pass all needed details as a single configuration object. Please refer to the documentation for all available properties.

Before (tipsi-stripe):

const items = [ {
label: 'Total',
amount: '50.00',
},
];
const shippingMethods = [
{
id: 'standard',
label: 'Standard Shipping',
detail: 'Arrives by June 29',
amount: '10.00',
},
];
const options = {
requiredBillingAddressFields: ['all'],
requiredShippingAddressFields: ['phone', 'postal_address'],
shippingMethods,
};
const token = await stripe.paymentRequestWithNativePay(items, options);
view raw gistfile1.ts hosted with ❤ by GitHub

After (stripe-react-native):

const { presentApplePay } = useApplePay()
// ...
const shippingMethods = [
{
identifier: 'standard',
detail: 'Arrives by June 29',
label: 'Standard Shipping',
amount: '10.0',
},
];
const items = [
{ label: 'Subtotal', amount: '15.00', type: 'final' },
{ label: 'Shipping', amount: '10.00', type: 'pending' },
{ label: 'Total', amount: '25.00', type: 'pending' },
];
const { error, paymentMethod } = await presentApplePay({
cartItems: items,
country: 'US',
currency: 'USD',
shippingMethods,
requiredShippingAddressFields: [
'emailAddress',
'phoneNumber',
'postalAddress',
'name',
],
requiredBillingContactFields: ['phoneNumber', 'name'],
jcbEnabled: true,
});
view raw gistfile1.ts hosted with ❤ by GitHub

Remember that the last item in items array needs to reflect the total value.

completeNativePayRequest

To process payment in Tipsi after requesting it, it is required to call completeNativePayRequest function. After migration there is a confirmApplePayPayment function for that. This and every other ApplePay related function you can get from useApplePay hook or import directly from the library.

Before (tipsi-stripe):

try {
stripe.completeNativePayRequest();
} catch (error) {
// ...
}
view raw gistfile1.ts hosted with ❤ by GitHub

After (stripe-react-native):

const { confirmApplePayPayment } = useApplePay()
// ...
const { error } = await confirmApplePayPayment('client_secret');
view raw gistfile1.ts hosted with ❤ by GitHub

GooglePay

As against to tipsi-stripe, stripe-react-native provides a separate API for GooglePay. In Tipsi, you could use the same functions as for ApplePay (see above) but with stripe-react-native there is a need to (first) initialize GooglePay and then (second) present it using  presentGooglePay function.

Before (tipsi-stripe):

const token = await stripe.paymentRequestWithNativePay({
total_price: '100.00',
currency_code: 'USD',
shipping_address_required: true,
phone_number_required: true,
shipping_countries: ['US', 'CA'],
line_items: [
{
currency_code: 'USD',
description: 'Whisky',
total_price: '50.00',
unit_price: '50.00',
quantity: '1',
},
],
});
view raw gistfile1.ts hosted with ❤ by GitHub

After (stripe-react-native):

const { initGooglePay, presentGooglePay } = useGooglePay()
// ...
const { error } = await initGooglePay({
merchantName: 'Widget Store'
countryCode: 'US',
billingAddressConfig: {
format: 'FULL',
isPhoneNumberRequired: true,
isRequired: false,
},
existingPaymentMethodRequired: false,
isEmailRequired: true,
});
if (error) {
// handle error
return;
}
const { error: presentError } = await presentGooglePay({
clientSecret,
forSetupIntent: true,
currencyCode: 'USD',
});
view raw gistfile1.ts hosted with ❤ by GitHub

After migrating, you can also create a payment method instead of confirming the payment. For that createGooglePayPaymentMethod can be used. Please refer to the documentation for more details.

CardForm

In Tipsi there is a paymentRequestWithCardForm  function that opens the Add Card view to accept a payment. Unfortunately, this is not supported in stripe-react-native. In order to collect card details please use CardField component or  confirmPaymentSheetPayment  functionality for a unify Payment UI experience.

Example

So looks like migration is not that scary anymore and it is quite doable, right? Show your work - as they say. Fortunately, I have an example and a proof for you.

There's an example PR with a Tipsi to stripe-react-native migration. This is a migration of a project with:

  • Card payment
  • Apple Pay usage
  • Google Pay usage
tipsi-stripe to stripe-react-native igration

Summary

These migration steps described above show these two libraries are noticeable and require you to refactor your app’s code. But remember, after you start the migration process you will see that the differences are not too big. You should be able to migrate almost everything quickly.

In the upcoming articles, we will be covering the testing part and answering most common questions. Thanks for reading and stay tuned!

Resources:

You can find all services offered by our React Native development company in the Services section.

Insights

Learn more about Open Source

cover

Aug 7Article

Visualizing Open Source Licenses to Analyze Compliance and Reduce Legal Risk

Discover how license-kit visualize maps and analyzes your dependency graph, helping you spot license issues, duplicates, and restrictive packages with interactive views, stats, and AI-powered summaries.

cover

Mar 26Podcast

How Definitely Typed Changed TypeScript Forever

Discover the story behind Definitely Typed with its creator, Boris Yankov, and learn how it became essential for type-safe JavaScript in the TypeScript ecosystem.

cover

Oct 14Article

Introducing the OMH Cloud Storage Module

Discover the OMH Cloud Storage Module. This open-source solution simplifies cloud storage integration for React Native apps.

cover

Jul 12Article

Introduction to the React Native OMH Maps Library

Discover how the React Native OMH Maps library simplifies map integration with cross-platform compatibility, ease of use, and customization options.

cover

Apr 22Podcast

Behind the Scenes of React Native 0.74 Release

Join the release crew members to find out what new features and improvements React Native 0.74 brings to the cross-platform development world.

cover

Apr 27Podcast

Callstack OSS Update

April 2023 Callstack OSS Update: RFC for Expo Config Plugins, Re.Pack & Paper news.

cover

Dec 6Article

React Native Paper 5.0 - What’s New?

React Native Paper 5.0 is here! Dive into the latest updates and improvements.

cover

Feb 8Article

Stripe React Native: the Most Common Questions - Tipsi Migration Guide, Part 5

Check out the list of the most common questions about Stripe react Native Library prepared by Jakub Kłobus, software engineer at Callstack.

cover

Dec 6Article

Stripe React Native Configuration - Tipsi Migration Guide, Part 2

In the second part of the Tipsi Migration Guide by Callstack, you'll learn how to properly configure the stripe-react-native library.

cover

Mar 19Tutorial

The Hard Way vs. The React Native AI SDK Way: Stop Writing Custom Modules for Every Model

Learn how React Native AI uses a unified provider model to switch between cloud and on-device LLMs without rewriting application logic, enabling reliable AI experiences online and offline.

cover

Mar 12Tutorial

Build Smarter Apps: Tool Calling & AI Orchestration Explained

Learn how on-device models in React Native can call tools, interact with external APIs, and return structured outputs you can use directly in application logic.

cover

Mar 5Tutorial

MLC LLM + React Native: On-Device AI Without the Pain

Learn how to run third-party on-device LLMs in React Native using MLC LLM. Choose your models, run them efficiently across platforms, and keep a unified JavaScript API with React Native AI.

cover

Feb 24Tutorial

Intelligent Fallbacks with Apple Intelligence in React Native

Learn how to use Apple’s built-in Foundation Models in React Native apps, reduce memory usage, and enable fast, fully on-device AI with React Native AI Apple.

cover

Feb 10Tutorial

What Is the React Native AI SDK? A Complete Intro & Quickstart

Learn why on-device AI matters for React Native apps, how local LLMs behave offline, and what problems React Native AI is designed to solve from day one.

cover

Jan 29Tutorial

Implementing an Android TurboModule with Kotlin

Learn how to implement a React Native TurboModule on Android, from the TypeScript spec and Codegen to the Kotlin implementation and package registration.

cover

Jan 19Tutorial

Working with Different Threads in Swift TurboModules

Learn how JavaScript, native module, background, and main threads interact in React Native TurboModules, and how to use GCD queues to run async work safely in Swift.

cover

Jan 9Tutorial

How to Add Type-Safe Constants to Swift TurboModules

Learn how to expose typed, immutable constants from a Swift TurboModule using Codegen, and get full end-to-end type safety between native and JavaScript.

cover

Dec 30Tutorial

Adding Event Emitters to Your TurboModule in Swift

Learn how to emit native events from a Swift TurboModule and subscribe to them in JavaScript, including proper memory management and Codegen integration.

cover

Dec 22Tutorial

Writing Your First TurboModule in Swift

Learn how to build a fully working TurboModule in Swift, integrate it with Codegen, and bridge it through Objective‑C inside an Expo app.