Other articles that helped me:
https://www.youtube.com/watch?v=oWK7kesoCQY&t=172s
Other cloud function methods
https://medium.com/@brinorw24/using-stripe-connect-with-flutterflow-1929346c6dc3
This is my code for cloud function payout method:
const functions = require('firebase-functions');
const admin = require("firebase-admin");
admin.initializeApp();
const axios = require('axios');
const stripe = require("stripe")(functions.config().stripe.token);
exports.stripeWebhook = functions.https.onRequest(async (req, res) => {
ย try {
ย ย const whSec = functions.config().stripe.payments_webhook_secret;
ย ย // Verify the Stripe webhook event signature
ย ย const event = stripe.webhooks.constructEvent(
ย ย ย req.rawBody,
ย ย ย req.headers["stripe-signature"],
ย ย ย whSec
ย ย );
ย ย if (event.type === 'checkout.session.completed') {
ย ย ย const session = event.data.object;
ย ย ย const stripeAccountID = session.client_reference_id;
ย ย ย const amountTotal = session.amount_total;
ย ย ย const paymentIntent = session.payment_intent;
ย ย ย const payeeRef = session.metadata.payeeRef;
ย ย ย const payorRef = session.metadata.payorRef;
ย ย ย const payeeUsername = session.metadata.payeeUsername;
ย ย ย // Construct and send a transfer API call
ย ย ย try {
ย ย ย ย const transferResponse = await axios.post('https://api.stripe.com/v1/transfers', {
ย ย ย ย ย amount: amountTotal,
ย ย ย ย ย currency: 'usd',
ย ย ย ย ย description: 'trush Tips',
ย ย ย ย ย destination: stripeAccountID,
ย ย ย ย ย // Add other relevant data
ย ย ย ย }, {
headers: {
ย 'Authorization': Bearer ${functions.config().stripe.token},
ย 'Content-Type': 'application/x-www-form-urlencoded'
}
ย ย ย ย });
ย ย ย ย console.log(transferResponse.data);
ย ย ย ย res.status(200).send('Tips transfer processed successfully');
ย ย ย ย ย
ย ย ย ย const transferID = transferResponse.data.id;
ย ย ย ย // Update Firestore document with success information
ย ย ย ย await admin.firestore().collection("tipOrPayout").doc().set({
ย ย ย ย ย tipOrPayout: "tips", // Adjust field name to lowercase with underscores
ย ย ย ย ย amount: amountTotal,
ย ย ย ย ย paymentIntentID: paymentIntent,
ย ย ย ย ย transferID: transferID,
ย ย ย ย ย payeeUsername: payeeUsername,
ย ย ย ย ย payeeRef: payeeRef,
ย ย ย ย ย payorRef: payorRef,
ย ย ย ย ย status: "Success", // Adjust to "Success" (capitalized first letter)
ย ย ย ย ย refund: false,
ย ย ย ย });
ย ย ย } catch (transferApiError) {
ย ย ย ย console.error('Transfer API call failed:', transferApiError);
ย ย ย ย res.status(500).send('Error processing webhook, transfer API call failed');
ย ย ย ย // Handle the refund API call and Firestore document update
ย ย ย ย try {
ย ย ย ย ย const refundResponse = await axios.post('https://api.stripe.com/v1/refunds', {
ย ย ย ย ย ย payment_intent: paymentIntent,
ย ย ย ย ย ย // Add other relevant data
ย ย ย ย ย }, {
ย headers: {
ย ย 'Authorization': Bearer ${functions.config().stripe.token},
ย ย 'Content-Type': 'application/x-www-form-urlencoded'
ย ย ย ย ย ย }
ย ย ย ย ย });
ย ย ย ย ย console.log(refundResponse.data);
ย ย ย ย ย // Update Firestore document with refund information
ย ย ย ย ย await admin.firestore().collection("tipOrPayout").doc().set({
ย ย ย ย ย ย tipOrPayout: "tips", // Adjust field name to lowercase with underscores
ย ย ย ย ย ย amount: amountTotal,ย
ย ย ย ย ย ย paymentIntentID: paymentIntent,
ย ย ย ย ย ย transferID: "n/a",
ย ย ย ย ย ย payeeUsername: payeeUsername,
ย ย ย ย ย ย payeeRef: payeeRef,
ย ย ย ย ย ย payorRef: payorRef,
ย ย ย ย ย ย status: "Refunded", // Adjust to "Refunded" (capitalized first letter)
ย ย ย ย ย ย refund: false,
ย ย ย ย ย });
ย ย ย ย } catch (refundApiError) {
ย ย ย ย ย console.error('Refund API call failed:', refundApiError);
ย ย ย ย ย // Handle refund API call errors here
ย ย ย ย ย // Update Firestore document with failed refund information
ย ย ย ย ย await admin.firestore().collection("tipOrPayout").doc().set({
ย ย ย ย ย ย tipOrPayout: "tips", // Adjust field name to lowercase with underscores
ย ย ย ย ย ย amount: amountTotal,
ย ย ย ย ย ย paymentIntentID: paymentIntent,
ย ย ย ย ย ย transferID: "n/a",
ย ย ย ย ย ย payeeUsername: payeeUsername, ย
ย ย ย ย ย ย payeeRef: payeeRef,
ย ย ย ย ย ย payorRef: payorRef,
ย ย ย ย ย ย status: "Failed", // Adjust to "Failed" (capitalized first letter)
ย ย ย ย ย ย refund: true,
ย ย ย ย ย });
ย ย ย ย }
ย ย ย }
ย ย } else {
ย ย ย res.status(400).send('Unhandled event type');
ย ย }
ย } catch (err) {
ย ย console.error("Webhook signature verification failed:", err);
ย ย res.sendStatus(400);
ย }
});