#salonmonster_error

1 messages ยท Page 1 of 1 (latest)

heavy deltaBOT
#

๐Ÿ‘‹ Welcome to your new thread!

โฒ๏ธ We'll be here soon! Typically we respond in a few minutes, but sometimes we might take a bit longer if the server is busy or if you have a particularly tricky question.

โฑ๏ธ We close idle threads, which makes them read-only. Once a thread is closed it won't be reopened, but you can always start a new thread if you have another question.

๐Ÿ”— This thread will always be available, even after it's closed. You can find it again using Discord's search, or you can save this link: https://discord.com/channels/841573134531821608/1362148508706013206

๐Ÿ“ Have more to share? Add more details, code, screenshots, videos, etc. below.

foggy lantern
#

Hi ๐Ÿ‘‹ thank you for that link, my first question was going to be what you meant by "connect link", whether you're building and sharing a link for OAuth onboarding, or if you were creating Accounts directly and generating Account Links for them. Seems like this is the former.

rocky turret
#

I notice when using the link that the user email is no longer being pre-filled in the strip e form

#

Correct, we're sending them directly to the link

#

from our web-app

#

This is happening to multiple users

#

But when we test ourselves it works for us

foggy lantern
#

Yup, needed to confirm whether this is OAuth or not, as that makes a difference in what we need to look at.

Just making sure I understand, you're trying to onboard users with existing Express Stripe accounts? That's what is led you down the OAuth path?

rocky turret
#

These are new users - this is the method we have used to create new stripe connect accounts for the past couple of years.

So they are a user of our app and we are trying to create their stripe account and then link it to our app

foggy lantern
#

Ah, gotcha, this isn't the flow I'd use for onboarding someone onto Stripe fresh, but sounds like you're maintaining a legacy flow.

Do you have an example of an Account that was able to onboard as expected?

rocky turret
#

Ok, that is helpful to know - maybe we should change the approach - we are very happy to do that. This is currently causing major headaches for us. It is how we were advised to build it by stripe and has been working for us up to this point. But is now failing intermittently.

Our code is:

    if (
      this.salon.address1 == '' ||
      this.salon.address1 == undefined ||
      this.salon.city == '' ||
      this.salon.city == undefined ||
      this.salon.province == '' ||
      this.salon.province == undefined ||
      this.salon.postal == '' ||
      this.salon.postal == undefined ||
      this.salon.postal == '' ||
      this.salon.postal == undefined ||
      this.salon.country == undefined
    ) {
      const modal = await this.modalCtrl.create({
        component: MailingAddressComponent,
        backdropDismiss: false,
      });

      modal.onDidDismiss().then(() => {
        this.salon = this.userService.getSalon().clone();
      });

      modal.present();
      return;
    }

    const userData = JSON.parse(localStorage.getItem('user'));
    const salonID = JSON.parse(localStorage.getItem('salonID'));
    const stripe_user: string = `stripe_user[email]=${userData['email']}&stripe_user[phone_number]=${userData['phone1']}&stripe_user[first_name]=${userData['firstName']}&stripe_user[last_name]=${userData['lastName']}&stripe_user[street_address]=${userData['address1']} ${userData['address2']}&stripe_user[zip]=${userData['postal']}`;
    window.location.href = `https://connect.stripe.com/express/oauth/authorize?response_type=code&client_id=${ENV_CONFIG.STRIPE_CLIENT_ID}&scope=read_write&state=${salonID}&${stripe_user}`; 
  }```

We are open to changing the process though.
#

We've lost several customers over the last few weeks due to this issue. We reached out to Stripe support via email and they told us we were doing everything correctly but that it was failing on Stripe's end. They have not responded to any of our recent messages though.

foggy lantern
rocky turret
#

Ok, that is my guess too.
I've been away for the last couple of weeks and just got passed this to solve.
I don't know how much logging there is - I can check into that.

#

So if I understand correctly, what is probably happening is:

  • we are sending the user to the stripe signup flow
  • the user completes the stripe form
  • the form passes back to our app
  • our app is not sending the final connect post to stripe
  • so the account is created but not associated
foggy lantern
#

For reference, this is the new onboarding flow that we recommend these days. With this approach you first create the Account object directly, then creating an Account Link for that Account (which is what generates the URL to our hosted onboarding flow)
https://docs.stripe.com/connect/express-accounts

But we can focus on trying to figure out what's going on here first.

Express connected accounts enable your platform to manage payout schedules, customize the flow of funds, and control branding. Stripe handles onboarding, account management, and identity verification.

foggy lantern
#

I do see evidence that the onboarding flow is being accessed and is resulting in updates being made to the Account and Person objects, just I'm not seeing an association between the Account and a Platform.

rocky turret
#

ok, and just to confirm: that is probably that our app is receiving the account and person objects back, but it isn't then passing those to stripe to do the final connect?

#

I'm just taking a peek at that code

foggy lantern
#

That's my initial thinking, since I didn't see any indication of a large-breakage on our end, and I would anticipate a break in this flow would be noticed on our side pretty quickly (but I could be mistaken!).

rocky turret
#

Ok, I'm just taking a look at the flow in the code here - will just be a minute

heavy deltaBOT
rocky turret
#

Ok, it looks like we get the data back from Stripe and check if we receive a 'code' and 'state' value:

    this.isLoading = true;
    this.errorOccurredWhileLoadingStats = false;
    var code;
    var state;
    this.route.queryParams.subscribe((params) => {
      code = params['code'];
      state = params['state'];
      if (code && state) {
        this.router.navigate([], {
          queryParams: {
            code: null,
            state: null,
          },
          queryParamsHandling: 'merge',
        });
        // this is the data returned from the stripe connect oauth flow for setting up a new stripe account
        this.stripeService.insertStripeAccountInfo({ code, state }).subscribe({
          next: async (res) => {
            this.isLoading = false;
            this.hasStripeAccount = true;
            // reload payment settings page without query parameters
            this.ref.detectChanges();
            // right now
            const toast = await this.toastCtrl.create({
              message: `Congratulations your payment processing is now setup and connected to deposit funds into your account.`,
              duration: 3000,
            });
            toast.present();

            if (window && window['Intercom']) {
              const intercom = window['Intercom'];
              intercom('update', { company: { Has_stripe_account: true } });
            }
          },
          error: (err) => {
            this.isLoading = false;
            this.errorOccurredWhileLoadingStats = true;
            this.ref.detectChanges();
            this.loadStripeData();
          },
        });
      } else {
        // if they have a stripe account, load it's data```
#

And pass that to a Stripe Service we have:

    params
  ): Observable<{ stripeAccountInfo: StripePayment }> {
    const url = `${ENV_CONFIG.API_ROOT}/stripe/oauth/callback?code=${params['code']}&state=${params['state']}`;
    return new Observable<{ stripeAccountInfo: StripePayment }>((observer) => {
      this.get(url).subscribe({
        next: (stripeAccountInfo) => {
          observer.next(stripeAccountInfo);
          observer.complete();
        },
        error: (err) => {
          observer.error(this.errorHandlerService.handleError(err));
          observer.complete();
        },
      });
    });
  }```
#

Then that calls our API:

  try {
    const { code, state } = req.query;
    const stripeInfo = await stripe.oauth.token({
      grant_type: "authorization_code",
      code,
    });
    delete stripeInfo.token_type;

    const response = await stripeModel.insert({ stripeInfo, state });
    // should return
    res.json({ data: response, success: true });
  } catch (e) {
    res.status(500).send(e);
  }
});```
fair ember
#

๐Ÿ‘‹ stepping in as toby needs to step away

rocky turret
#

Hi Bismark

fair ember
#

One of the reasons we migrated away from this flow and it is considered our legacy flow now is that if your user closes the page after hitting the "submit" button on the Hosted Onboarding flow but before the redirect occurs then this connection could just not take place as you are seeing now.

#

Everything you shared looks fine to me

#

But the reality is that if the return URL is never reached then the connection is never completed.

rocky turret
#

Gotcha

fair ember
#

Overall this isn't a large change/migration.

#

The main thing is that now you create the Connected Account upfront using the API which means it is immediately connected to your platform.

rocky turret
#

We definitely want 100% connection. This is causing major headaches right now and we have lost several large user accounts because they are sick of it not completing their stripe signup ๐Ÿ˜ฌ

I just got back from vacation today and the team hear is so frustrated so they roped me in to try and find a solution

#

We'll definitely migrate to the new flow

#

The last piece of our current flow is saving the stripe account details to our db:


    const deferred = Q.defer<StripeAccountInformation>();
    stripeInfo = {...stripeInfo, state};

    this.getPoolConnection(async (err: Error, poolConnection: PoolConnection) => {
        if (err) {
            poolConnection.release();
            deferred.reject(new MySQLError(err));
            return;
          }
          try {

          await this.insertStripeData(poolConnection, stripeInfo)
           
          poolConnection.release();
          deferred.resolve()
        } catch (e) {
            poolConnection.release();
            return deferred.reject(e);
        }
    });
    return deferred.promise;

}```
#

But from what you're saying: we're not doing nything wrong .... BUT if there is an issue on the user's end that interrupts the flow the connection will NOT happen right now

#

And that is probably what we're seeing

#

So instead if we swap to this new flow, we should see 100% connection success

fair ember
#

Yes

rocky turret
#

And that this is a known issue for the legacy flow we're using

fair ember
#

I can't say 100% that it is a user issue versus some blip in your own side of things for when the user reaches your return URL -- you would need logging to confirm that -- but overall this is a known issue that can happen and is why we highly recommend our new flow here.

rocky turret
#

OK, gotcha.

Just a heads up that the main Stripe support team might need to be briefed on this: my team has been trying to solve this with them since April 2nd and has had over 5 phone calls with them and no solution. At no point during those discussions was this mentioned: https://dashboard.stripe.com/support/sco_S3gHY1qawDoALs

#

Either way, we'll implement this new flow right away!

fair ember
#

I'm sorry to hear that :(. I'll pass on feedback internally about that.

rocky turret
#

Thanks, I appreciate it. Don't want anyone else suffering with this ๐Ÿ™‚

#

We'll get that implemented and let you know how that goes.

#

Although I guess I can't actually let you know as this chat will be closed ๐Ÿ˜› but thanks for the help

fair ember
#

Feel free to open a new thread at any point if we can help any further!