#aaron_api

1 messages ยท Page 1 of 1 (latest)

lusty heathBOT
#

๐Ÿ‘‹ 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/1286396069030395935

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

Below are links to other discussions we've had with you in the past week in case you want to review that information. If your question is related to one of these previous discussions, please provide a comprehensive summary of the current state and what you need help with now. We help many users simultaneously, so a summary allows us to resolve your issue as soon as possible.

narrow urchin
#

Hey, this is related to the aaron_code thread from last night

serene meadow
#

We don't typically go back to previous threads, can you summarize the issue you're having here?

narrow urchin
#

Essentually we're getting an invalid_grant response from an API call on a user using our Stripe App

#

It seems totally random

#

It will work perfectly for a few hours, then we'll hit an invalid_grant, which on our end invalidates their Stripe connection

#

And the invalid_grant error is something like this

  "error": "invalid_grant",
  "error_description": "Refresh token does not exist: rt_Qqb********************************"
}```
#

I don't even know how we'd ever get a refresh token that doesn't exist

serene meadow
#

Can you give me the request ID for the request showing that error?

narrow urchin
#

Sure

#

req_6EYGeJDokRZVQW req_XpIJZAc9jvxJSr

serene meadow
#

Looking...

lusty heathBOT
serene meadow
#

From what I can see it looks like an invalid refresh token was provided. You're 100% certain the correct refresh token was provided in this request?

narrow urchin
#

I'm not sure of anything, but we'd only ever be passing a refresh token we received from the user during auth/when we refresh their tokens from the tokens endpoint

#
    async #refreshToken() {
        // user must be connected to stripe
        if (!this.user.stripeRefreshToken) {
            throw new StripeNotConnectedError("Stripe account not connected.");
        }
        const now = Date.now();
        if (this.user.stripeAccessTokenExpiresAt && this.user.stripeAccessTokenExpiresAt > now) {
            return await crypto.decrypt(this.user.stripeAccessToken);
        } else {
            // build request
            const secretKey = await keys.stripeSecretKey;
            console.log(
                "stripeSecretKey", secretKey,
                "refreshToken", await crypto.decrypt(this.user.stripeRefreshToken),
                "accessToken", await crypto.decrypt(this.user.stripeAccessToken),
            )
            const headers = {
                "Authorization": "Basic " + Buffer.from(`${secretKey}:`).toString("base64"),
                "Content-Type": "application/x-www-form-urlencoded"
            };
            const bodyParams = {
                refresh_token: await crypto.decrypt(this.user.stripeRefreshToken),
                grant_type: "refresh_token"
            };
            const accessTokenExpiresAt = Date.now() + ONE_HOUR;
            const response = await fetch(StripeUrls.stripeRefreshUrl(), {
                method: "POST",
                headers: headers,
                body: new URLSearchParams(bodyParams).toString()
            });
            const responseData = await response.json();
            // disconnect user from stripe if refresh_token is invalid or expired
            if (responseData["error"] === "invalid_grant") {
                this.user = await weivData.update("Users", {
                    ...this.user,
                    stripeRefreshToken: null,
                    stripeAccessToken: null,
                    stripeAccessTokenExpiresAt: null,
                    stripeUserId: null
                });
                EmailClient.sendStripeDisconnectedEmail(this.user);
                console.error("StripeInvalidGrantError", responseData);
                throw new StripeInvalidGrantError(responseData["error_description"]);
            } else {
                const refreshToken = responseData["refresh_token"];
                const accessToken = responseData["access_token"];
                console.log("responseData", responseData);
                if (refreshToken && accessToken) {
                    this.user = await weivData.update("Users", {
                        ...this.user,
                        stripeRefreshToken: await crypto.encrypt(refreshToken),
                        stripeAccessToken: await crypto.encrypt(accessToken),
                        stripeAccessTokenExpiresAt: accessTokenExpiresAt
                    });
                    return accessToken;
                } else {
                    throw new Error("Unable to access Stripe user.");
                }
            }
        }```
#

Does anything here look weird on how we'd be using refresh tokens?

limber delta
#

Hey again ๐Ÿ‘‹ jumping in as my teammate needs to step away. Offhand I'm not spotting anything in that code that looks off, it looks like it's following our guidelines. It also looks like you're captuing the new refresh token that is generated when you exchange the refresh token.

narrow urchin
#

My thoughts yesterday was that there might be a race condition, but then we realized that that error code is different

#

I honestly have no idea how to even debug this further my dude

limber delta
#

I'm at a bit of a loss too. Is there any pattern in the accounts that it happens to?

I'm trying to think of additional things to ask that may point us in the right direction, but this may need to be turned into a support ticket so we can dive in further.

narrow urchin
#

We only have 2 accounts connected I'm afraid, and both experience it. Not a huge sample size hahaha

#

We use their saved refresh/access token within the expiration time, then ocasionally when we renew it we get that error

#

Sometimes we renew it and it is totally fine

#

On my last ticket they gave me the idea to try again on the error, and see if it just wasnt in the stripe database yet

limber delta
#

Hhmmmm, I can see why you were thinking race condition then, like maybe two refresh requests went off too close to each other

narrow urchin
#

exactly but the error is different so i ended up writing that one off

#

like This authorization code has already been used. All tokens issued with this code have been revoked.

#

Oh wait maybe that's just the auth code from initial auth error

#

I have no idea brother hahahah

#

maybe the error isnt different

limber delta
#

Honestly I'm at a bit of a loss too. This isn't a flow I'm super familiar with. I'm going to poke around internally and see if anyone else has reported this error, and if I can pull any insight from that if so.

narrow urchin
#

I appreciate that

limber delta
#

Hm, the only mentions I'm finding of that error are from back when these were Connect Extensions rather than Stripe Apps.

#

I think at this point trying to add more logging to your flow may be the best path forward to get more insight. I'm not exactly sure what could be happening, but my rough thinking is maybe either a refresh token isn't being stored and an old one is being used (though again, maybe that throws the other error you shared), or maybe the token is somehow getting updated incorrectly.

narrow urchin
#

Would it still apply if it was from Connect Extentions? Is the OAuth flow the same?

#

We log the users old credentials, then the response from the refresh and honestly don't see anything wrong, but again I'm no expert

limber delta
#

They're similar (there was no useful information on the discussion either, it was just two comments ๐Ÿ˜… )

narrow urchin
#

dang

#

Do you think a retry on error is a potential solution or do you think if it doesn't exist it probably won't exist on round 2?

limber delta
#

I think it wouldn't work the second time around if it doesn't exist the first time. BUT, if it does, that likely points to an issue on our end that we could flag to our teams.

narrow urchin
#

hmm, might be worth a try adding that logic

#

is it possible the Parent API key in our App could be being passed wrong sometimes? Or do you reckon that'd be an entirely different error

#

Like if we pass undefined or something as the Platform, I can see how that key technically wouldn't exist, but i also don't know if the request would even make it that far on an invalid Platform key

limber delta
#

I'd expect that to throw a different, and I wouldn't expect you to be able to find the request logs if that were the case since we wouldn't know to associate the request with your account if you're not using your key.

narrow urchin
#

right great point

#

Okay this is off-topic but I have another question hahaha

#

If I make a request to say create a customer with an idempotency key that's already been used, what will the response of that request look like?

#

Will I be able to tell it's an idempotency error?

#

I can't really find anything in the docs about it

limber delta
#

There won't necessarily be an error thrown, what happens in that case is you get the exact same response that the first request generated.

Our idemptoency keys are meant to be used to handle intermittent network issues where you make a request and never get a response back. In those cases you don't know if the request made it to us and just the response got lost, or if the request got lost too. So you make the same request reusing the same idempotency key. If the request never made it to us, we run it like it's the first time you made the request. If we did get the first request, we send the same response that request generated.

We talk about it here:
https://docs.stripe.com/api/idempotent_requests

narrow urchin
#

Awesome, that's exactly what I was hoping the response was like

#

thank you brotha

#

also at what point does making a ticket about this Stripe Apps issue make sense?

limber delta
#

You could do it soon. If you can get more information to include with that it may help it go a bit better, but I think if you describe what you're seeing now we can try to take a deeper look.

My biggest fear if you open it with the information you have right now is that the answer will be that the refresh token doesn't exist, which would be frustrating to hear I'm sure. If you can get logs showing it does in fact exist, like here's the request where I got the refresh token rt_a1b2c3 from, here's the request where I consumed it before it's expiration timeframe, that'll help show that it's not the token getting corrupted on your end somehow.