You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native集成Stripe报错:无法找到Elements上下文

React Native集成Stripe报错:找不到Elements上下文

我要给React Native应用集成Stripe,这个应用是已完成Stripe集成且运行正常的桌面端应用的移动端版本,后端已经实现。原本想复用React Web应用里的上下文,但发现@stripe/stripe-react-native没有useElement,于是改用@stripe/stripe-js。现在一直报错:

Could not find Elements context; You need to wrap the part of your app that calls useElements() in an provider.

但我已经用<Elements>组件包裹了表单,以下是我的代码:

checkoutForm.js

import { useNavigation } from '@react-navigation/native';
import React, { useState, useEffect, useRef } from 'react'

import { 
    View, 
    Text, 
    SafeAreaView, 
    StatusBar, 
    StyleSheet, 
    TouchableOpacity, 
    ScrollView, 
    Image,
    Pressable , 
    TextInput, 
    Alert} 
from 'react-native';

import Icon from '../components'
import { COLORS } from '../constants';
import { useStateContext } from '../context/StateContext';
import { ProductCarousel } from '../../components';
import { useElements, Elements} from "@stripe/react-stripe-js"
import { CardField, useConfirmPayment, useStripe,  } from '@stripe/stripe-react-native';
import { Button } from 'react-native-elements';


const CheckoutForm = () => {

  const navigation = useNavigation();
  const [isDeliveryAddressOpen, setIsDeliveryAddressOpen] = useState(false);
  const [isContactNumberOpen, setIsContactNumberOpen] = useState(false);
  const [isDeliveryInstructionsOpen, setIsDeliveryInstructionsOpen] = useState(false);
  const [isCartItemsOpen, setIsCartItemsOpen] = useState(false);  
  const [isPaymentInfoOpen, setIsPaymentInfoOpen] = useState(false);
  //const { confirmPayment, loading } = useConfirmPayment();

  const [success, setSuccess ] = useState(false)
  const stripe = useStripe()
  const elements = useElements()
  const cardElement = useRef(null);

  const {  totalPrice,  cartItems } = useStateContext();

  const [fullName, setFullName] = useState('');
  const [email, setEmail] = useState('');
  const [address, setAddress] = useState('');
  const [address2, setAddress2] = useState('');  
  const [state, setState] = useState('');
  const [city, setCity] = useState('');
  const [zipCode, setZipCode] = useState('');
  const [primaryNumber, setPrimaryNumber] = useState('');
  const [SecondaryNumber, setSecondaryNumber] = useState('');
  const [DeliveryInstructions, setDeliveryInstructions] = useState('');   

  const [isProcessing, setIsProcessing] = useState(false);
  const [isError, setIsError] = useState(false);
  //const [cardDetails, setCardDetails] = useState('')



  const toggleDeliveryAddress = () => {
    setIsDeliveryAddressOpen(!isDeliveryAddressOpen);
  };

  const toggleContactNumber = () => {
    setIsContactNumberOpen(!isContactNumberOpen);
  };

  const toggleDeliveryInstructions = () => {
    setIsDeliveryInstructionsOpen(!isDeliveryInstructionsOpen);
  };

  const toggleCartItems = () => {
    setIsCartItemsOpen(!isCartItemsOpen);
  };

  const togglePaymentInfo = () => {
    setIsPaymentInfoOpen(!isPaymentInfoOpen);
  };


 //const navigate = useNavigate();
  const carts = { 
    status: 'pending', 
    items: (cartItems), 
    address, 
    fullName, 
    zipCode, 
    state,
    city,
    DeliveryInstructions, 
    primaryNumber, 
    SecondaryNumber, 
    totalPrice
  }

  const handleCheckout = async () => {
    
    const response = await fetch('/create_cart', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(carts),
    });

    if(response.statusCode === 500) return;
    // eslint-disable-next-line
    const data = await response.json();
    console.log('presseddddd', data)
  }  

 /*========================================================*/

 const handleSubmit = async () => {
        
    const {error, paymentMethod} = await stripe.createPaymentMethod({
        type: "card",
        card: elements.getElement(CardField),
        billing_details: {
            name: fullName,
            phone: primaryNumber,
            email: email,
            address: {
              city: city,
              line1: address,
              state: state,
              postal_code: zipCode
            }
          },
    })

    if(!error) {
        try {
            const {id} = paymentMethod
            
            const carts = { 
              id, 
              amount: totalPrice, 
              confirm: true, 
              currency: 'CAD'
            }

            setIsProcessing(true);
            
            const response = await fetch('/create-payment-intent', {
                method: 'POST',
                headers: {
                'Content-Type': 'application/json',
                },
                body: JSON.stringify(carts),
            });


            if(response.status === 200) {
                console.log(response.status)
                console.log("Successful payment")
                setSuccess(true)
            }

            const data = await response.json();

            if(response.status >= 400) {
              console.log(response.status)
              console.log(data.error.split(':').pop())
              setIsError(data.error)
          }

        } catch (error) {
            console.log("Error", error)
        }
    } else {
        console.log(error.message)

    }

    setIsProcessing(false)

    
    //navigate('/');
  }



  return (
    <View style={styles.container}>
        <View style={styles.insideContainer}>         
        <TouchableOpacity style={styles.titleContainer} onPress={togglePaymentInfo}>
            <Text style={styles.title}>Payment Info</Text>
            {!isPaymentInfoOpen ? 
            <Icon icon ='add-outline' color='#000' size={20}/>
            : <Icon icon ='remove-outline' color='#000' size={20}/> 
            }
        </TouchableOpacity>
        {isPaymentInfoOpen && (
            <View style={styles.containercollapsed}>
                <View style={{width: '98%'}}>
                <CardField
                    ref={cardElement}
                    postalCodeEnabled={false}
                    placeholders={{
                    number: '4242 4242 4242 4242',
                    }}
                    cardStyle={{
                    backgroundColor: '#FFFFFF',
                    textColor: '#000000',
                    borderColor: COLORS.lightGray2,
                    borderWidth: 1,
                    borderRadius: 4
                    }}
                    style={{
                    width: '100%',
                    height: 50,
                            
                    }}
                    onCardChange={(cardDetails) => {
                    }}
                    onFocus={(focusedField) => {
                    }}
                />
                </View>                
            </View>
        )}
        </View>   

        <View style={styles.PaybuttonView}>
        <Button
          title="Pay"
          onPress={ () => handleSubmit()}
          /*disabled={loading}
          loading={loading}*/
          />
          {/*<Pressable style={styles.Paybutton} onPress={() => {handleCheckout(); handlePayPress()} }>
            <Text style={{textAlign: 'center', fontSize: 20, color: '#FFFF', textTransform: 'uppercase'}}>Checkout</Text>
              </Pressable>*/}
        </View>           

  </View>
  )
}

export default CheckoutForm

StripeForm.js

import React from 'react'
import {Elements} from "@stripe/react-stripe-js"
import CheckoutForm from './CheckoutForm';
import { loadStripe } from "@stripe/stripe-js"

const stripePromise = loadStripe('这里是我的密钥,我暂时去掉了');

export const StipeForm = () => {
    return (
        <Elements stripe={stripePromise}>
            <CheckoutForm />
        </Elements>
    ) 
}

问题原因及修复方案

核心错误

你混用了Web端和React Native端的Stripe SDK,两者上下文体系不兼容:

  • @stripe/stripe-react-native是专为React Native设计的独立SDK,有自己的上下文管理逻辑
  • 你同时导入了Web端的useElements/Elements和Native端的CardField/useStripe,导致Web端的Elements无法为Native端的钩子提供正确上下文

修复步骤

  1. 清理依赖:卸载@stripe/stripe-js和@stripe/react-stripe-js,只保留@stripe/stripe-react-native
  2. 替换上下文提供者:用Native SDK的StripeProvider包裹整个应用(而非Web端的Elements)
    // 根组件示例(如App.js)
    import { StripeProvider } from '@stripe/stripe-react-native';
    
    export default function App() {
      return (
        <StripeProvider
          publishableKey="你的Stripe公钥"
          merchantIdentifier="merchant.com.yourappname" // Apple Pay配置,可选
        >
          {/* 你的导航或应用内容 */}
          <StripeForm />
        </StripeProvider>
      );
    }
    
  3. 调整StripeForm组件:移除Web端的Elements,直接渲染CheckoutForm
    import React from 'react'
    import CheckoutForm from './CheckoutForm';
    
    export const StripeForm = () => {
        return <CheckoutForm />;
    }
    
  4. 修改CheckoutForm逻辑:移除Web端导入,使用Native SDK的API
    // 移除这行:import { useElements, Elements} from "@stripe/react-stripe-js"
    import { CardField, useStripe } from '@stripe/stripe-react-native';
    
    const CheckoutForm = () => {
      // 移除这行:const elements = useElements()
    
      const handleSubmit = async () => {
        const stripe = useStripe();
        // Native SDK无需通过elements获取CardField,直接调用createPaymentMethod即可读取卡片数据
        const { error, paymentMethod } = await stripe.createPaymentMethod({
          type: 'card',
          billing_details: {
            name: fullName,
            phone: primaryNumber,
            email: email,
            address: {
              city: city,
              line1: address,
              state: state,
              postal_code: zipCode
            }
          },
        });
    
        // 后续支付流程保持不变
      }
    }
    

额外注意

  • 后端Payment Intent逻辑无需修改,和Web端兼容
  • 测试使用Stripe官方测试卡号:4242 4242 4242 4242

内容的提问来源于stack exchange,提问作者Ons Jannet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 14:35:11