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