flutter - Connect user to stripe connect to allow him receiving money - Stack Overflow

时间: 2025-01-06 admin 业界

I have a flutter app and I try to connect user to stripe connect so he is able to receive money and me to get 10 % of his sells but when I try to initiate the functionality I receive this error

`0      StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:646:7)
#1      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:334:18)
<asynchronous suspension>
#2      MethodChannelHttpsCallable.call (package:cloud_functions_platform_interface/src/method_channel/method_channel_https_callable.dart:22:24)
<asynchronous suspension>
#3      HttpsCallable.call (package:cloud_functions/src/https_callable.dart:49:37)
<asynchronous suspension>
#4      _PaymentMethodState._testFunction (package:influfit/PaymentMethod.dart:122:22)
<asynchronous suspension>
flutter: Stack trace: #0      StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:646:7)
#1      MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:334:18)
<asynchronous suspension>
#2      MethodChannelHttpsCallable.call (package:cloud_functions_platform_interface/src/method_channel/method_channel_https_callable.dart:22:24)
<asynchronous suspension>
#3      HttpsCallable.call (package:cloud_functions/src/https_callable.dart:49:37)
<asynchronous suspension>
#4      _PaymentMethodState._testFunction (package:influfit/PaymentMethod.dart:122:22)
<asynchronous suspension>
flutter: Starting Stripe account creation for user: 8k4b52239lRqFlunTHQuWkO9ZfS2
flutter: User email: [email protected]
flutter: Firebase Functions Error - Code: -1004, Message: Could not connect to the server., Details: null
`

This is the code that I am using to do this and this is the index.js with cloud functionalities I use ... can someone recommend what to do or another option?

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_functions/cloud_functions.dart';

class PaymentMethod extends StatefulWidget {
  @override
  _PaymentMethodState createState() => _PaymentMethodState();
}

class _PaymentMethodState extends State<PaymentMethod> {
  bool _isLoading = false;
  String? _error;
  String? _stripeStatus;
  User? _currentUser;
  late FirebaseFunctions functions;

  @override
  void initState() {
    super.initState();
    // Initialize Firebase Functions with region
    functions = FirebaseFunctions.instanceFor(region: 'us-central1');
    _initializeAuth();
  }

  Future<void> _initializeAuth() async {
    // Listen to auth state changes
    FirebaseAuth.instance.authStateChanges().listen((User? user) {
      setState(() {
        _currentUser = user;
      });
      if (user != null) {
        _checkStripeStatus();
      }
    });
  }

  Future<void> _checkStripeStatus() async {
    try {
      final result = await functions.httpsCallable('checkStripeAccount').call();
      
      if (result.data['accountId'] != null) {
        setState(() {
          _stripeStatus = 'Connected to Stripe';
        });
      }
    } catch (e) {
      print('Error checking Stripe status: $e');
    }
  }

  Future<void> _createStripeAccount() async {
    if (!mounted) return;
    
    setState(() {
      _isLoading = true;
      _error = null;
    });

    try {
      final user = FirebaseAuth.instance.currentUser;
      if (user == null) {
        throw Exception('User not logged in');
      }

      // Get fresh ID token
      await user.getIdToken(true);

      print('Starting Stripe account creation for user: ${user.uid}');
      print('User email: ${user.email}');
      
      final result = await functions.httpsCallable(
        'createStripeAccount',
        options: HttpsCallableOptions(
          timeout: const Duration(seconds: 60),
        ),
      ).call({
        'email': user.email,
        'userId': user.uid,
      });

      print('Raw response: ${result.data}');

      if (result.data['success'] == true && result.data['accountLink'] != null) {
        final accountLink = result.data['accountLink'];
        print('Account link received: $accountLink');
        setState(() {
          _stripeStatus = 'Stripe account created successfully';
        });
      }
      
    } on FirebaseFunctionsException catch (e) {
      print('Firebase Functions Error - Code: ${e.code}, Message: ${e.message}, Details: ${e.details}');
      setState(() {
        _error = 'Error: ${e.message}';
      });
    } catch (e, stackTrace) {
      print('Error creating Stripe account: $e');
      print('Stack trace: $stackTrace');
      setState(() {
        _error = 'Error: ${e.toString()}';
      });
    } finally {
      if (mounted) {
        setState(() => _isLoading = false);
      }
    }
  }

  Future<void> _testFunction() async {
    try {
      final user = FirebaseAuth.instance.currentUser;
      if (user == null) {
        print('No user logged in');
        return;
      }

      // Get fresh ID token
      final idToken = await user.getIdToken(true);
      print('Got fresh ID token');
      
      final result = await functions.httpsCallable('testFunction').call({
        'test': 'data',
        'userId': user.uid,
      });
      
      print('Test function response: ${result.data}');
    } catch (e, stack) {
      print('Test function error: $e');
      print('Stack trace: $stack');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.white,
        iconTheme: IconThemeData(color: Colors.black),
        title: Text(
          'Payment Method',
          style: TextStyle(
            color: Colors.black,
            fontFamily: 'Montserrat',
            fontSize: 16,
            fontWeight: FontWeight.w600,
          ),
        ),
        elevation: 0,
      ),
      body: _currentUser == null
        ? Center(
            child: Text('Please log in to continue'),
          )
        : Container(
            padding: EdgeInsets.all(16),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                ElevatedButton(
                  onPressed: _testFunction,
                  child: Text('Test Firebase Function'),
                ),
                SizedBox(height: 16),
                Text(
                  'Stripe Connect Account',
                  style: TextStyle(
                    fontSize: 18,
                    fontWeight: FontWeight.bold,
                    fontFamily: 'Montserrat',
                  ),
                ),
                SizedBox(height: 16),
                if (_stripeStatus != null)
                  Container(
                    padding: EdgeInsets.all(8),
                    decoration: BoxDecoration(
                      color: Colors.green.shade50,
                      borderRadius: BorderRadius.circular(8),
                    ),
                    child: Row(
                      children: [
                        Icon(Icons.check_circle, color: Colors.green),
                        SizedBox(width: 8),
                        Text(
                          _stripeStatus!,
                          style: TextStyle(
                            color: Colors.green,
                            fontFamily: 'Montserrat',
                          ),
                        ),
                      ],
                    ),
                  )
                else
                  Text(
                    'Connect your bank account to receive payments',
                    style: TextStyle(fontFamily: 'Montserrat'),
                  ),
                SizedBox(height: 16),
                if (_error != null)
                  Container(
                    padding: EdgeInsets.all(8),
                    margin: EdgeInsets.only(bottom: 16),
                    color: Colors.red.shade100,
                    child: Text(
                      _error!,
                      style: TextStyle(
                        color: Colors.red,
                        fontFamily: 'Montserrat',
                      ),
                    ),
                  ),
                if (_isLoading)
                  Center(child: CircularProgressIndicator())
                else if (_stripeStatus == null)
                  ElevatedButton(
                    onPressed: _createStripeAccount,
                    child: Text('Connect with Stripe'),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.blue,
                      padding: EdgeInsets.symmetric(
                        horizontal: 32,
                        vertical: 12,
                      ),
                    ),
                  ),
              ],
            ),
          ),
    );
  }
}

and my index.js file :

const functions = require('firebase-functions');
const admin = require('firebase-admin');
const stripe = require('stripe')(functions.config().stripe.secret);

admin.initializeApp();

// Check if user has a Stripe account
exports.checkStripeAccount = functions.https.onCall(async (data, context) => {
  if (!context.auth) {
    throw new functions.https.HttpsError('unauthenticated', 'User must be logged in');
  }

  try {
    const userId = context.auth.uid;
    const userDoc = await admin.firestore()
      .collection('users')
      .doc(userId)
      .get();

    const stripeAccountId = userDoc.data()?.stripeAccountId;
    
    if (stripeAccountId) {
      const account = await stripe.accounts.retrieve(stripeAccountId);
      return { accountId: account.id };
    }

    return { accountId: null };
  } catch (error) {
    console.error("Error checking Stripe account:", error);
    throw new functions.https.HttpsError('internal', error.message);
  }
});

// Create Stripe Connect account
exports.createStripeAccount = functions.https.onCall(async (data, context) => {
  try {
    if (!context.auth) {
      throw new functions.https.HttpsError('unauthenticated', 'Must be logged in');
    }

    const userId = context.auth.uid;
    const userEmail = data.email;

    if (!userEmail) {
      throw new functions.https.HttpsError('invalid-argument', 'Email is required');
    }

    // Create Stripe account
    const account = await stripe.accounts.create({
      type: 'express',
      email: userEmail,
      capabilities: {
        card_payments: {requested: true},
        transfers: {requested: true},
      },
    });

    // Save to Firestore
    await admin.firestore()
      .collection('users')
      .doc(userId)
      .set({
        stripeAccountId: account.id,
        stripeAccountCreatedAt: admin.firestore.FieldValue.serverTimestamp(),
      }, { merge: true });

    // Create account link
    const accountLink = await stripe.accountLinks.create({
      account: account.id,
      refresh_url: `/stripe/refresh`,
      return_url: `/stripe/success`,
      type: 'account_onboarding',
    });

    return {
      success: true,
      accountId: account.id,
      accountLink: accountLink.url
    };

  } catch (error) {
    console.error("Error creating Stripe account:", error);
    throw new functions.https.HttpsError('internal', error.message);
  }
});