I developed an app in React Native that performs a financial transaction with Stripe. The function below is responsible for accessing the endpoint of the Node.js server I developed. There is no complexity, it's something simple. In the emulator, I can access the elements (payment screen) and get success in the transaction, but in the build (preview mode), the application closes unexpectedly without warning and without any notification. Below is the code for the server and React Native.
const buy = async () => {
try {
const finalAmount = amount;
const response = await fetch('https://app-production.up.railway.app/buy', {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
coin: `App - ${priceList[selectedId].time_session}`,
quantity: quantity,
amount: finalAmount,
}),
});
const data = await response.json();
if (!response.ok) {
return Alert.alert(data.message);
}
const initSheet = await stripe.initPaymentSheet({
paymentIntentClientSecret: data.clientSecret,
merchantDisplayName: "App",
});
if (initSheet.error) {
return Alert.alert(initSheet.error.message);
}
const presentSheet = await stripe.presentPaymentSheet();
if (presentSheet.error) {
return Alert.alert(presentSheet.error.message);
}
Alert.alert("Payment successful!");
addIncomeTransaction()
// Update user data in Firestore
const userRef = doc(db, 'users', userID);
await updateDoc(userRef, {
'user_billing.balance': totalCoins + amount,
'user_billing.plan_aquisition': PlanAquired,
});
setTotalCoins(totalCoins + amount);
setQuantity(1);
} catch (err) {
Alert.alert("Error", err.message);
}
};
App.tsx
return (
<StripeProvider publishableKey={process.env.EXPO_PUBLIC_STRIPE_KEY}>
<ReduxProvider store={Store}>
<PaperProvider>
<GestureHandlerRootView style={{ flex: 1 }} onLayout={onLayoutRootView}>
<Router />
</GestureHandlerRootView>
</PaperProvider>
</ReduxProvider>
</StripeProvider>
);
Server below
require("dotenv").config();
const express = require("express");
const app = express();
const Stripe = require("stripe");
const stripe = Stripe(process.env.STRIPE_SECRET_KEY);
const cors = require("cors");
const PORT = process.env.PORT || 4242;
app.use("/stripe", express.raw({ type: "*/*" }));
app.use(express.json());
app.use(cors({
origin: 'https://App-production.up.railway.app/'
}));
app.post("/buy", async (req, res) => {
try {
// Getting data from client
let { coin, quantity, amount } = req.body;
// Simple validation
if (!coin || !quantity || !amount)
return res.status(400).json({ message: "Invalid data, try again" });
amount = parseInt(amount);
// Initiate payment
const paymentIntent = await stripe.paymentIntents.create({
amount: Math.round(amount * 100) / 10 * 10, // Amount in cents ex: 10.90 -> 1090
currency: 'brl',
payment_method_types: ["card"],
metadata: { coin, quantity, amount },
});
// Extracting the client secret
const clientSecret = paymentIntent.client_secret;
// Sending the client secret as response
res.json({ message: "Initializing payment", clientSecret });
} catch (err) {
// Catch any error and send error 500 to client
console.error(err);
res.status(500).json({ message: "Internal server error." });
}
});
// Webhook endpoint
app.post("/stripe", async (req, res) => {
// Get the signature from the headers
const sig = req.headers["stripe-signature"];
let event;
try {
// Check if the event is sent from Stripe or a third party
// And parse the event
event = await stripe.webhooks.constructEvent(
req.body,
sig,
process.env.STRIPE_WEBHOOK_SECRET
);
} catch (err) {
// Handle what happens if the event is not from Stripe
console.log(err);
return res.status(400).json({ message: err.message });
}
// Event when a payment is initiated
if (event.type === "payment_intent.created") {
console.log(`${event.data.object.metadata.coin} payment initated!`);
}
// Event when a payment is succeeded
if (event.type === "payment_intent.succeeded") {
// fulfilment
console.log(`${event.data.object.metadata.coin} payment successfuly!`);
}
res.json({ ok: true });
});
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));