How to Connect and Pay to Stripe Merchant Accounts Using Flutter and Firebase
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 thestripe_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:
- LinkedIn: LinkedIn Profile
- GitHub: GitHub Profile