Stripe payment integration React Native. calling presentPaymentSheet function leads to app crash

Viewed 616

I am building an e-commerce app using React Native and Django as backend. The payment integration is built with Stripe. The following lines of code are from Django views, whose aim is to create a customer, ephemeral key, and payment intent and return their specific values which will be needed to complete the transactions...

@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def payment_sheet(request, *args, **kwargs):

    data = request.data
    subtotal = data['subtotal']
    customer = stripe.Customer.create()
    ephemeralKey = stripe.EphemeralKey.create(
    customer=customer['id'],
    stripe_version='2020-08-27'
    )
    paymentIntent = stripe.PaymentIntent.create(
        amount=int(subtotal),
        currency='eur',
        customer=customer['id'],
    )
    return JsonResponse({
        'paymentIntent': paymentIntent.client_secret,
        'ephemeralKey': ephemeralKey.secret,
        'customer': customer.id
    })

My screen is within the StripeProvider as Docs guides.

const MainFlow = ({ }) => {

return (
    <ShoppingProvider>
        <StripeProvider
            publishableKey="pk_test_Dt4ZBItXSZT1EzmOd8yCxonL"
        >
            <OrderItemProvider>
                <MainFlowStack.Navigator>
                    <MainFlowStack.Screen name="Home" component={HomeScreen} options={{ headerShown: false }} />
                    <MainFlowStack.Screen name="Cart" component={CartScreen} options={{ headerShown: false }} />
                    <MainFlowStack.Screen name="Favourite" component={FavouriteScreen} options={{ headerShown: false }} />
                    <MainFlowStack.Screen name="Search" component={SearchScreen} options={{ headerShown: false }} />
                    <MainFlowStack.Screen name="Detail" component={DetailScreen} options={{ headerShown: false }} />
                    <MainFlowStack.Screen name="Checkout" component={CheckoutScreen} options={{ headerShown: false }} />
                </MainFlowStack.Navigator>
            </OrderItemProvider>
        </StripeProvider>
    </ShoppingProvider>
)

On the other hand, here is the code from my Checkout Screen from my React Native project.

const CheckoutScreen = ({ route }) => {

const { token } = useUserInfo()
const { initPaymentSheet, presentPaymentSheet } = useStripe();
const [clientSecret, setClientSecret] = useState('')
const [loading, setLoading] = useState(false);
const subtotal = route.params.subtotal

const fetchPaymentSheetData = (token, subtotal) => {

    const total = (subtotal * 100).toString()
    const data = { "subtotal": total }

    return fetch(`${API}/api/payment/payment-sheet/`, {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            'Authorization': `Token ${token}`
        },
        body: JSON.stringify(data)
    })
        .then((res) => {
            return res.json()
        })
        .catch((err) => console.log(err))
}

const initializePaymentSheet = (token, subtotal) => {

    fetchPaymentSheetData(token, subtotal)
        .then((res) => {
            setClientSecret(res.paymentIntent)
            initPaymentSheet({
                customerId: res.customer,
                customerEphemeralKeySecret: res.ephemeralKey,
                paymentIntentClientSecret: res.paymentIntent,
            })
                .then((res) => {
                    console.log(res)
                    setLoading(true)
                })
                .catch((err) => console.log(err))
        })
        .catch((err) => console.log(err))

    // const { error } = await initPaymentSheet({
    //     customerId: paymentInfo.customer,
    //     customerEphemeralKeySecret: paymentInfo.ephemeralKey,
    //     paymentIntentClientSecret: paymentInfo.paymentIntent,
    // })
    // if (!error) {
    //     setLoading(true)
    // }
}

const openPaymentSheet = async () => {
    const { error } = await presentPaymentSheet({ clientSecret });

    if (error) {
        Alert.alert(`Error code: ${error.code}`, error.message);
    } else {
        Alert.alert('Success', 'Your order is confirmed!');
    }
};

useEffect(() => {
    initializePaymentSheet(token, subtotal)
}, [])

return (
    <View style={style.root}>
        <Navbar />
        <Text>{subtotal}</Text>
        <Button
            variant="primary"
            disabled={!loading}
            title="Checkout"
            onPress={openPaymentSheet}
        />
    </View>
);

}

The initPaymentSheet promise is not rejected and the 'Checkout' Button is now available. The problem comes when the button is pressed and hence within the 'openPaymentSheet' function, presentPaymentSheet is called... this leads to a crashing in the app. Can anyone point out a bug or tell why this error is happening?

Thanks in advance

1 Answers

I fell into the same problem, and for me the problem was solved by providing some additional information while initPaymentSheet.

   const {error} = await initPaymentSheet({
      customerEphemeralKeySecret: ephemeralKey,
      setupIntentClientSecret: setupIntent,
      customerId: customer,
      customFlow: false,
      merchantDisplayName: 'Test Inc.',
      style: 'alwaysDark',
    });

packages versions are:

"@stripe/stripe-react-native": "^0.13.1",
"react-native": "^0.68.2",

I was following the bellow document to implement the stripe

https://stripe.com/docs/payments/save-and-reuse

Related