I'm developing an app with Expo, Firebase Cloud Function and Stripe, for some reason the first time I navigate to the screen CardPayment
the code returns the error: [Unhandled promise rejection: SyntaxError: JSON Parse error: Unexpected identifier "Error"]
, but if I refresh the screen the code runs perfectly. Anyone have any idea why and how I can fix it? I've tried a bunch of stuff, even using axios, but nothing seems to be working.
Backend
exports.addCardForExistingCustomer = functions.https.onRequest(async (req, res) => {
const ephemeralKey = await stripe.ephemeralKeys.create(
{customer: `${req.query.customer}`},
{apiVersion: '2020-08-27'}
);
const setupIntent = await stripe.setupIntents.create({
customer: `${req.query.customer}`
});
const paymentMethods = await stripe.paymentMethods.list({
customer: `${req.query.customer}`,
type: 'card',
});
res.json({
setupIntent: setupIntent.client_secret,
ephemeralKey: ephemeralKey.secret,
customer: `${req.query.customer}`,
paymentMethods: paymentMethods,
})
});
Frontend
export default function CardPayment() {
const { initPaymentSheet, presentPaymentSheet } = useStripe();
const [loading, setLoading] = useState(false);
const [customerId, setCustomerId] = useState('');
const fetchPaymentSheetParams = async () => {
const response = await fetch(`https://<<path>>.cloudfunctions.net/addCardForExistingCustomer?customer=${customerId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
});
const { setupIntent, ephemeralKey, customer } = await response.json();
return {
setupIntent,
ephemeralKey,
customer,
};
};
const initializePaymentSheet = async () => {
const {
setupIntent,
ephemeralKey,
customer,
} = await fetchPaymentSheetParams();
console.log(setupIntent)
const { error } = await initPaymentSheet({
customerId: customer,
customerEphemeralKeySecret: ephemeralKey,
setupIntentClientSecret: setupIntent,
});
if (!error) {
setLoading(true);
} else {
console.log(error)
}
};
const openPaymentSheet = async () => {
const { error } = await presentPaymentSheet();
if (!error) {
Alert.alert('Success', 'Your payment method is successfully set up for future payments!');
}
};
const fetchCustomer = async () => {
const fetchInformation = doc(db, 'clients', 'user', 'Information', auth.currentUser.uid)
await getDoc(fetchInformation)
.then((snapshot) => {
const stripeId = snapshot.data().stripeCustomerId
setCustomerId(stripeId)
initializePaymentSheet();
})
}
useEffect(() => {
fetchCustomer()
}, []);
if (loading == false) {
return (
<View>
<ActivityIndicator/>
</View>
)
} else {
return (
<View>
<CustomButton
buttonTxt={'Add Credit/Debit Card'}
txtColor={'white'}
backgroundColor={'black'}
onPress={openPaymentSheet}
/>
</View>
)
}
}
CodePudding user response:
fetchCustomer
on the frontend isn't waiting for the getDoc
promise chain to complete. Try putting return
or await
before the call to getDoc
. If return
is used, fetchCustomer
as posted doesn't need to be an async
function.
If this resolves the error, my guess would be that some kind of response caching is allowing the then
handler of getDoc(fetchInformation)
to win a race condition set up in promise job handling (in the microtask queue) when the page is reloaded.
The getDoc
call handler (async (snapshot) => {...}
) is missing an await
operator before the initializePaymentSheet();
call. Again this could set up an unpredictable race condition in the microtask queue if initializePaymentSheet
is delayed.