Tarvo Mäesepp
Tarvo Mäesepp

Reputation: 4533

Stripe Connect transfer: Insufficient funds

I am trying to implement Stripe's Connect into my application. I've done hours of research and trial and error method debugging and now I am in situation where I get no technical errors but error that says:

Insufficient funds in Stripe account. In test mode, you can add funds to your available balance (bypassing your pending balance) by creating a charge with 4000 0000 0000 0077 as the card number. You can use the the /v1/balance endpoint to view your Stripe balance (for more details, see stripe.com/docs/api#balance).

The paymentIntent in Stripe Dashboar show me PaymentIntent status: requires_confirmation

The error for me seems weird since The card I am testing with is exactly the one they suggested me to use. Note that I've also tried to use other cards.

I am using Google Cloud Functions as a backend for my Stipe API.

This is the function that creates account and customer. I am creating both of them just to be sure everything is working fine.

// When a user is created in firebase auth, register them with Stripe
exports.createStripeUser = functions.auth.user().onCreate(async (user) => {
  const account = await stripe.accounts.create({type: 'custom', business_type: 'individual', individual: {email: user.email}, requested_capabilities: ['card_payments', 'transfers'], email: user.email});
  const customer = await stripe.customers.create({email: user.email});
  return admin.firestore().collection('stripe_customers').doc(user.uid).set({account_id: account.id, customer_id: customer.id});
});

Now I am adding card information:

// Add a payment source (card) for a user by writing a stripe payment source token to Cloud Firestore
exports.addPaymentSource = functions.firestore.document('/stripe_customers/{userId}/tokens/{pushId}').onCreate(async (snap, context) => {
  const source = snap.data();
  const token = source.token;
  if (source === null){
    return null;
  }

  try {
    const snapshot = await admin.firestore().collection('stripe_customers').doc(context.params.userId).get();
    const customer =  snapshot.data().customer_id;
    const response = await stripe.customers.createSource(customer, {source: token});
    return admin.firestore().collection('stripe_customers').doc(context.params.userId).collection("sources").doc(response.fingerprint).set(response, {merge: true});
  } catch (error) {
    await snap.ref.set({'error':userFacingMessage(error)},{merge:true});
    return reportError(error, {user: context.params.userId});
  }
});

This is how I create my paymentIntent:

// Create Stripe paymentIntent whenever an amount is created in Cloud Firestore
exports.createStripePaymentIntent = functions.firestore.document('stripe_customers/{userId}/charges/{id}').onCreate(async (snap, context) => {
      const val = snap.data();
      try {
        // Look up the Stripe customer id written in createStripeUser
        const snapshot = await admin.firestore().collection(`stripe_customers`).doc(context.params.userId).get()
        const snapval = snapshot.data();
        const customer = snapval.customer_id
        const amount = val.amount;
        const charge = {amount, currency, customer, transfer_group: val.transfer_group, payment_method: val.payment_method};
        if (val.source !== null) {
          charge.source = val.source;
        }
        const response = await stripe.paymentIntents.create(charge);
        // If the result is successful, write it back to the database
        return snap.ref.set(response, { merge: true });
      } catch(error) {
        // We want to capture errors and render them in a user-friendly way, while
        // still logging an exception with StackDriver
        console.log(error);
        await snap.ref.set({error: userFacingMessage(error)}, { merge: true });
        return reportError(error, {user: context.params.userId});
      }
    });

Since now everything seems to work as expected and now comes in the fun part, the TRANSFER. Which I am not able to make because of the error mentioned above. This is how I create my charge:

exports.createStripeTransfer = functions.firestore.document('stripe_customers/{userId}/transfers/{id}').onCreate(async (snap, context) => {
  const val = snap.data();
  try {
    // Look up the Stripe account id written in createStripeUser
    const snapshot = await admin.firestore().collection(`stripe_customers`).doc(context.params.userId).get()
    const snapval = snapshot.data();

    const destinationAccount = val.destination
    const amount = val.amount;
    const charge = {amount, currency, destination: destinationAccount, transfer_group: val.transfer_group};
    if (val.source !== null) {
      charge.source = val.source;
    }
    const response = await stripe.transfers.create(charge);
    stripe.paymentIntents.confirm(response.id, {payment_method: response.payment_method})
    // If the result is successful, write it back to the database
    return snap.ref.set(response, { merge: true });
  } catch(error) {
    // We want to capture errors and render them in a user-friendly way, while
    // still logging an exception with StackDriver
    console.log(error);
    await snap.ref.set({error: userFacingMessage(error)}, { merge: true });
    return reportError(error, {user: context.params.userId});
  }
});

Can anybody explain me what am I missing here? Why am I getting the error? I tried to manually top up account which did not help also.

Update 1: According to Sergio Tulentsev's comment it seems I gotta confirm the transfer for it to succeed. So I did implement the following line after successful transfer but the error remains the same:

stripe.paymentIntents.confirm(response.id, {payment_method: response.payment_method})

What is the difference between stripe.paymentIntent.confirm vs stripe.confirmCardPayment?

Upvotes: 0

Views: 3076

Answers (1)

Tej Viramgama
Tej Viramgama

Reputation: 135

It seems like you have access to dashboard and you have test account. In this case you can manually add funds from Payments -> New and then provide test card details that goes like as shown in photo attached and card number used here is 4000 0000 0000 0077.

As you mention you are generating payment Intent this will only credit the charges amount to your available funds only after you have a valid live account added till then the funds will always be on HOLD.

So for testing you can add funds manually as shown in linkpic of generating new payment manually

Upvotes: 4

Related Questions