|
| 1 | +# Accept a payment - classic |
| 2 | + |
| 3 | +Collecting payments in your React Native app consists of three steps. Creating an object to track payment on your server, collecting card information in your app, and submitting the payment to Stripe for processing. |
| 4 | + |
| 5 | +Stripe uses this payment object, called a `PaymentIntent`, to track and handle all the states of the payment until it’s completed—even when the bank requires customer intervention, like two-factor authentication. |
| 6 | + |
| 7 | +## Setup Stripe |
| 8 | + |
| 9 | +### Client side |
| 10 | + |
| 11 | +The [React Native SDK](https://github.com/stripe/stripe-react-native) is open source and fully documented. Internally, it makes use of [native iOS](https://github.com/stripe/stripe-ios) and [Android](https://github.com/stripe/stripe-android) SDKs. Install the SDK by running: |
| 12 | + |
| 13 | +```sh |
| 14 | +yarn add react-native-stripe-sdk |
| 15 | +``` |
| 16 | + |
| 17 | +For iOS, run `pod install` in the `ios` directory to ensure that you also install the required native dependencies. Android doesn’t require any additional steps. |
| 18 | + |
| 19 | +When your app starts, configure the SDK with your Stripe [publishable key](https://stripe.com/dashboard.stripe.com/account/apikeys) so it can make requests to the Stripe API: |
| 20 | + |
| 21 | +```tsx |
| 22 | +import { StripeProvider } from '@stripe/stripe-react-native'; |
| 23 | + |
| 24 | +function App() { |
| 25 | + return ( |
| 26 | + <StripeProvider |
| 27 | + publishableKey="pk_test_51AROWSJX9HHJ5bycpEUP9dK39tXufyuWogSUdeweyZEXy3LC7M8yc5d9NlQ96fRCVL0BlAu7Nqt4V7N5xZjJnrkp005fDiTMIr" |
| 28 | + urlScheme="your-url-scheme" // required for 3D Secure and bank redirects |
| 29 | + merchantIdentifier="merchant.com.{{YOUR_APP_NAME}}" // required for Apple Pay |
| 30 | + > |
| 31 | + // Your app code here |
| 32 | + </StripeProvider> |
| 33 | + ); |
| 34 | +} |
| 35 | +``` |
| 36 | + |
| 37 | +## Create your checkout page |
| 38 | + |
| 39 | +Securely collect card information on the client with `CardForm` component. |
| 40 | + |
| 41 | +Add `CardForm` component to your payment screen. To collect card details you can use `onFormComplete` prop and save received data in component state.. |
| 42 | + |
| 43 | +```tsx |
| 44 | +function PaymentScreen() { |
| 45 | + const [card, setCard] = useState<CardFormView.Details>(); |
| 46 | + |
| 47 | + // ... |
| 48 | + |
| 49 | + return ( |
| 50 | + <View> |
| 51 | + <CardForm |
| 52 | + cardStyle={{ |
| 53 | + backgroundColor: '#FFFFFF', |
| 54 | + }} |
| 55 | + style={{ |
| 56 | + width: '100%', |
| 57 | + height: 300, |
| 58 | + }} |
| 59 | + onFormComplete={(cardDetails) => { |
| 60 | + setCard(cardDetails); |
| 61 | + }} |
| 62 | + /> |
| 63 | + </View> |
| 64 | + ); |
| 65 | +} |
| 66 | +``` |
| 67 | + |
| 68 | +## Create a PaymentIntent |
| 69 | + |
| 70 | +Stripe uses a [PaymentIntent](https://stripe.com/docs/api/payment_intents) object to represent your intent to collect payment from a customer, tracking your charge attempts and payment state changes throughout the process. |
| 71 | + |
| 72 | +### Client side |
| 73 | + |
| 74 | +On the client, request a PaymentIntent from your server and store its [client secret](https://stripe.com/docs/api/payment_intents/object#payment_intent_object-client_secret): |
| 75 | + |
| 76 | +```tsx |
| 77 | +function PaymentScreen() { |
| 78 | + // ... |
| 79 | + |
| 80 | + const fetchPaymentIntentClientSecret = useCallback(async () => { |
| 81 | + const response = await fetch(`${API_URL}/create-payment-intent`, { |
| 82 | + method: 'POST', |
| 83 | + headers: { |
| 84 | + 'Content-Type': 'application/json', |
| 85 | + }, |
| 86 | + body: JSON.stringify({ |
| 87 | + currency: 'usd', |
| 88 | + items: ['id-1'], |
| 89 | + }), |
| 90 | + }); |
| 91 | + const { clientSecret } = await response.json(); |
| 92 | + |
| 93 | + return clientSecret; |
| 94 | + }, []); |
| 95 | + |
| 96 | + const handlePayPress = useCallback(async () => { |
| 97 | + if (!card) { |
| 98 | + return; |
| 99 | + } |
| 100 | + |
| 101 | + try { |
| 102 | + // Fetch Intent Client Secret from backend |
| 103 | + const clientSecret = await fetchPaymentIntentClientSecret(); |
| 104 | + |
| 105 | + // ... |
| 106 | + } catch (e) { |
| 107 | + // ... |
| 108 | + } |
| 109 | + }, [card, fetchPaymentIntentClientSecret]); |
| 110 | + |
| 111 | + return ( |
| 112 | + <View> |
| 113 | + <CardForm |
| 114 | + onFormComplete={(cardDetails) => { |
| 115 | + setCardDetails(cardDetails); |
| 116 | + }} |
| 117 | + /> |
| 118 | + |
| 119 | + <Button onPress={handlePayPress} title="Pay" disabled={loading} /> |
| 120 | + </View> |
| 121 | + ); |
| 122 | +} |
| 123 | +``` |
| 124 | + |
| 125 | +## Submit the payment to Stripe |
| 126 | + |
| 127 | +When the customer taps **Pay**, confirm the `PaymentIntent` to complete the payment. |
| 128 | + |
| 129 | +Rather than sending the entire PaymentIntent object to the client, use its [client secret](https://stripe.com/docs/api/payment_intents/object#payment_intent_object-client_secret). This is different from your API keys that authenticate Stripe API requests. The client secret should be handled carefully because it can complete the charge. Don’t log it, embed it in URLs, or expose it to anyone but the customer. |
| 130 | + |
| 131 | +Pass the customer card details and client secret to `confirmPayment`, which you can access with the `useConfirmPayment` hook. The hook also returns a loading value to indicate the state of the asynchronous action: |
| 132 | + |
| 133 | +```tsx |
| 134 | +function PaymentScreen() { |
| 135 | + const { confirmPayment, loading } = useConfirmPayment(); |
| 136 | + |
| 137 | + // ... |
| 138 | + |
| 139 | + const handlePayPress = async () => { |
| 140 | + // Gather the customer's billing information (e.g., email) |
| 141 | + const billingDetails: BillingDetails = { |
| 142 | + email: 'jenny.rosen@example.com', |
| 143 | + }; |
| 144 | + |
| 145 | + // Fetch the intent client secret from the backend |
| 146 | + const clientSecret = await fetchPaymentIntentClientSecret(); |
| 147 | + |
| 148 | + // Confirm the payment with the card details |
| 149 | + const { paymentIntent, error } = await confirmPayment(clientSecret, { |
| 150 | + paymentMethodType: 'Card', |
| 151 | + paymentMethodData: { |
| 152 | + billingDetails, |
| 153 | + }, |
| 154 | + }); |
| 155 | + |
| 156 | + if (error) { |
| 157 | + console.log('Payment confirmation error', error); |
| 158 | + } else if (paymentIntent) { |
| 159 | + console.log('Success from promise', paymentIntent); |
| 160 | + } |
| 161 | + }; |
| 162 | + |
| 163 | + return <View />; |
| 164 | +} |
| 165 | +``` |
| 166 | + |
| 167 | +If authentication is required by regulation (e.g., Strong Customer Authentication), additional actions are presented to the customer to complete the payment process. See [Card authentication and 3D Secure](https://stripe.com/docs/payments/3d-secure) for more details. |
| 168 | + |
| 169 | +When the payment completes successfully, the value of the returned PaymentIntent’s `status` property is `succeeded`. Check the status of a PaymentIntent in the [Dashboard](https://dashboard.stripe.com/test/payments) or by inspecting the `status` property on the object. If the payment isn’t successful, inspect the returned `error` to determine the cause. |
| 170 | + |
| 171 | +## Test the integration |
0 commit comments