How to Connect and Pay to Stripe Merchant Accounts Using Flutter and Firebase

Basit Ali
4 min readNov 18, 2024

--

Introduction

Connecting to multiple sellers and facilitating payments on their behalf can open new opportunities for apps focused on marketplaces and services. In this guide, we’ll walk through how to integrate Stripe Connect for handling payments to multiple sellers in a Flutter app. This setup will enable each seller to receive payment for their specific products or services.

Our example will cover:

  • Connecting a seller’s Stripe account.
  • Storing the seller’s Stripe account ID.
  • Processing payments to multiple sellers at once.

Prerequisites

Before starting, ensure you have:

  • A Firebase project and Firebase Cloud Functions enabled.
  • A Stripe account and access to Stripe Connect.
  • The flutter_stripe package for integrating Stripe in Flutter.

Step 1: Set Up Your Stripe API Keys and Firebase Cloud Functions

To connect to Stripe and facilitate payments, you’ll need your publishable and secret keys from Stripe. Add these to your Firebase Cloud Functions config to keep them secure.

firebase functions:config:set stripe.secret_key="sk_test_yourSecretKey" stripe.client_id="ca_yourClientId"

In your Cloud Functions setup, this allow you to access Stripe securely with:

const stripe = require('stripe')(functions.config().stripe.secret_key);

Step 2: Connecting to Stripe from the Flutter App

To enable sellers to connect their accounts, we’ll set up an OAuth flow to Stripe. This will generate an access_token and stripe_user_id for each seller, which we’ll store in Firebase for processing future payments.

Flutter Code: Redirect to Stripe OAuth

This code enables your app to connect sellers to Stripe using OAuth. Replace the redirect_uri and client_id with the ones specific to your Stripe and Firebase configurations.

final String stripeClientId = 'ca_R9k4oiSuxVz13O4iT42MYJFQy6a1G8IR';
final String baseRedirectUri = 'uri to cloud function for stripe connect';

Future<void> _connectWithStripe() async {
var auth = FirebaseAuth.instance.currentUser!.uid;
final String encodedUserId = Uri.encodeComponent(auth);

final Uri stripeUrl = Uri.parse(
'https://connect.stripe.com/oauth/authorize'
'?redirect_uri=$baseRedirectUri'
'&client_id=$stripeClientId'
'&state=$encodedUserId'
'&response_type=code'
'&scope=read_write'
'&stripe_user[country]=DE',
);

EasyLauncher.url(url: stripeUrl.toString());
}

Step 3: Firebase Cloud Function to Handle OAuth Callback

The OAuth callback function handles the response from Stripe, extracting the authorization code and using it to get the stripe_user_id. This ID is saved in Firebase for later use in multi-seller payments.

exports.stripeOAuthCallback = functions.https.onRequest(async (req, res) => {
const authorizationCode = req.query.code;

try {
const response = await axios.post('https://connect.stripe.com/oauth/token', null, {
params: {
client_id: functions.config().stripe.client_id,
client_secret: functions.config().stripe.secret_key,
code: authorizationCode,
grant_type: 'authorization_code',
},
});

const stripeAccountId = response.data.stripe_user_id;
const userId = req.query.state;

if (userId) {
await admin.firestore().collection('Sellers').doc(userId).update({
stripeAccountId: stripeAccountId,
});
res.redirect('yourapp://oauth/callback');
} else {
res.status(400).send('User ID is missing.');
}
} catch (error) {
console.error('Error during Stripe OAuth callback:', error);
res.status(500).send({ success: false, error: error.message });
}
});

In this function:

  • authorizationCode is used to obtain the stripe_user_id.
  • The stripe_user_id is saved in Firebase for that seller.
  • Upon completion, the user is redirected back to the app.

Step 4: Storing Seller Information in Firestore

When a seller connects to Stripe, their stripe_user_id is stored in Firestore. This ID will be used for processing payments on behalf of that seller.

Step 5: Processing Payments for Multiple Sellers

To handle payments across multiple sellers, we’ll create a function that takes in a list of seller accounts and payment amounts. Each payment will then be routed to the corresponding Stripe account.

Flutter Code: Initializing Multi-Seller Payment

The following code allows you to pass a list of sellers to initiate a payment. Each seller should include their stripeAccountId and the amount they should receive.

bool _isLoading = false;

Future<void> initMultiSellerPayment(List<Map<String, dynamic>> sellers) async {
setState(() {
_isLoading = true;
});

try {
final response = await http.post(
Uri.parse("uri to cloud function for multiple sellers payment"),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({'sellers': sellers}),
);

final jsonResponse = jsonDecode(response.body);
if (jsonResponse['success'] == true) {
for (var paymentResult in jsonResponse['paymentResults']) {
if (paymentResult['success'] == true) {
final paymentIntentClientSecret = paymentResult['paymentIntent'];
await Stripe.instance.initPaymentSheet(
paymentSheetParameters: SetupPaymentSheetParameters(
paymentIntentClientSecret: paymentIntentClientSecret,
merchantDisplayName: 'Multi-Seller Service',
),
);
await Stripe.instance.presentPaymentSheet();
print("Payment successful for seller ${paymentResult['seller']}");
} else {
print("Payment failed for seller ${paymentResult['seller']}: ${paymentResult['message']}");
}
}
showSuccessDialog();
} else {
throw Exception('Failed to initialize payment intents for sellers.');
}
} catch (e) {
print("Error in multi-seller payment: $e");
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("Error in multi-seller payment")));
} finally {
setState(() {
_isLoading = false;
});
}
}

Cloud Function: Handling Multi-Seller Payment Intents

This Cloud Function processes payments to each seller’s Stripe account using the account ID saved in Firestore.

exports.stripeMultiSellerPaymentIntent = functions.https.onRequest(async (req, res) => {
try {
const sellers = req.body.sellers;

if (!sellers || !Array.isArray(sellers)) {
res.status(400).send({ success: false, message: "Invalid sellers data format" });
return;
}

const paymentResults = [];

for (const seller of sellers) {
const { stripeAccountId, amount } = seller;
if (!stripeAccountId || !amount) {
paymentResults.push({ success: false, message: "Missing stripeAccountId or amount", seller });
continue;
}

const paymentIntent = await stripe.paymentIntents.create({
amount: parseInt(amount),
currency: "usd",
payment_method_types: ["card"],
transfer_data: { destination: stripeAccountId },
description: `Payment for Seller ${stripeAccountId}`,
});

paymentResults.push({
success: true,
paymentIntent: paymentIntent.client_secret,
seller: stripeAccountId,
});
}

res.status(200).send({ success: true, paymentResults });
} catch (error) {
console.error("Error processing multi-seller payments:", error);
res.status(500).send({ success: false, error: error.message });
}
});

This function loops through each seller, creating a payment intent for each and transferring the specified amount to the seller’s account.

Conclusion

In this article, we’ve covered how to set up a multi-seller payment flow using Stripe Connect, Firebase, and Flutter. This approach allows you to easily onboard new sellers and manage payments for marketplace-style apps. With this setup, you can grow your app into a full-fledged marketplace with reliable and flexible payment capabilities.

Connect with Me

If you found this article helpful and want to connect, feel free to reach out:

--

--

Basit Ali
Basit Ali

Written by Basit Ali

Experienced Flutter Developer | Creating Elegant and Performant Cross-Platform Mobile Apps

Responses (1)