React Native+Stripe API开发数字钱包的技术疑问求助
Hey there! Let's walk through how to get your React Native + Stripe wallet management setup sorted out—since you're already using a server as a middle layer, we can lean into that to keep things secure and compliant, which is critical when handling sensitive card data.
First and foremost: never, ever send raw card details (number, CVV, expiration) to your own server. Stripe's entire ecosystem is built to handle PCI compliance for you, so we'll use their SDK to tokenize card data directly on the client, then pass only that secure token/identifier to your backend.
1. Adding Cards the Right Way
Use Stripe's React Native SDK to collect card info safely, no raw data touches your servers. Here's a step-by-step implementation:
Client-Side (React Native)
We'll use CardField for secure input and PaymentSheet to handle card verification via a Setup Intent (Stripe's tool for saving payment methods without charging immediately):
import { CardField, useStripe } from '@stripe/stripe-react-native'; import { View, Button, Alert } from 'react-native'; const AddCardScreen = ({ customerId }) => { const { initPaymentSheet, presentPaymentSheet } = useStripe(); const handleAddCard = async () => { try { // Step 1: Fetch an ephemeral key from your server (short-lived, secure access to Stripe) const ephemeralRes = await fetch('/api/stripe/get-ephemeral-key', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ customerId }), }); const ephemeralKey = await ephemeralRes.json(); // Step 2: Fetch a Setup Intent client secret from your server const setupIntentRes = await fetch('/api/stripe/create-setup-intent', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ customerId }), }); const setupIntent = await setupIntentRes.json(); // Step 3: Initialize the PaymentSheet const { error: initError } = await initPaymentSheet({ customerId, customerEphemeralKeySecret: ephemeralKey.secret, setupIntentClientSecret: setupIntent.clientSecret, }); if (initError) throw initError; // Step 4: Present the PaymentSheet to confirm card addition const { error: presentError } = await presentPaymentSheet(); if (presentError) throw presentError; // Success! Card is saved to Stripe—refresh your wallet list Alert.alert('Success', 'Card added to your wallet'); // Trigger a refresh of your wallet UI here } catch (err) { Alert.alert('Oops', err.message || 'Failed to add card'); } }; return ( <View style={{ padding: 20 }}> <CardField postalCodeEnabled={true} placeholder={{ number: '4242 4242 4242 4242' }} cardStyle={{ backgroundColor: '#fff', textColor: '#000' }} style={{ height: 50, marginVertical: 20 }} /> <Button title="Add Card" onPress={handleAddCard} /> </View> ); }; export default AddCardScreen;
Server-Side (Example with Node.js/Express)
You need two endpoints here: one to generate the ephemeral key, and another to create the Setup Intent:
const stripe = require('stripe')('YOUR_STRIPE_SECRET_KEY'); const express = require('express'); const app = express(); app.use(express.json()); // Endpoint to get ephemeral key app.post('/api/stripe/get-ephemeral-key', async (req, res) => { const { customerId } = req.body; const ephemeralKey = await stripe.ephemeralKeys.create( { customer: customerId }, { apiVersion: '2024-06-20' } // Use your Stripe API version ); res.json(ephemeralKey); }); // Endpoint to create Setup Intent app.post('/api/stripe/create-setup-intent', async (req, res) => { const { customerId } = req.body; const setupIntent = await stripe.setupIntents.create({ customer: customerId, payment_method_types: ['card'], }); res.json({ clientSecret: setupIntent.client_secret }); });
2. Managing Wallet: Fetch & Delete Cards
Fetching Saved Cards
Your server will fetch the user's saved payment methods from Stripe, sanitize the data (only return non-sensitive info), and send it to the client:
Server-Side
app.get('/api/user/wallet/cards', async (req, res) => { const { customerId } = req.query; try { const paymentMethods = await stripe.paymentMethods.list({ customer: customerId, type: 'card', }); // Sanitize data—never send full card numbers or CVVs! const sanitizedCards = paymentMethods.data.map(method => ({ id: method.id, last4: method.card.last4, brand: method.card.brand, expMonth: method.card.exp_month, expYear: method.card.exp_year, })); res.json(sanitizedCards); } catch (err) { res.status(500).json({ error: err.message }); } });
Client-Side
Call this endpoint when your wallet screen loads, then render the cards in a list:
// In your WalletScreen component useEffect(() => { const fetchCards = async () => { const res = await fetch(`/api/user/wallet/cards?customerId=${customerId}`); const cards = await res.json(); setWalletCards(cards); }; fetchCards(); }, [customerId]);
Deleting a Card
Send the payment method ID to your server, which will detach it from the Stripe customer:
Server-Side
app.delete('/api/user/wallet/card', async (req, res) => { const { paymentMethodId } = req.body; try { await stripe.paymentMethods.detach(paymentMethodId); res.json({ success: true }); } catch (err) { res.status(500).json({ error: err.message }); } });
Client-Side
Add a delete button to each card in your list, call this endpoint on press, then update the local state:
const handleDeleteCard = async (paymentMethodId) => { const res = await fetch('/api/user/wallet/card', { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ paymentMethodId }), }); if (res.ok) { setWalletCards(prev => prev.filter(card => card.id !== paymentMethodId)); Alert.alert('Success', 'Card removed'); } };
3. Critical Pitfalls to Avoid
- Don't cache ephemeral keys: They expire quickly (usually 24 hours max), so fetch a new one every time you need to interact with Stripe from the client.
- PCI Compliance: By using Stripe's SDK, you're automatically reducing your PCI scope—if you ever handle raw card data, you'll have to go through full PCI audits, which is a massive headache.
- Error Handling: Stripe returns detailed error messages (e.g., "card declined", "invalid CVV")—use these to give users clear, actionable feedback instead of generic errors.
- Link Users to Stripe Customers: Make sure every user in your app has a corresponding Stripe Customer object. This is how you tie payment methods to specific users.
4. UX Tips for Your Wallet
- Show brand icons (Visa, Mastercard, etc.) next to each card—Stripe provides the brand in the payment method data, so you can map that to icons in your app.
- Let users set a default payment method: Update the Stripe Customer's
invoice_settings.default_payment_methodvia your server, then highlight that card in your UI. - Add loading states when fetching/deleting cards to keep users informed.
内容的提问来源于stack exchange,提问作者nik2o




