#aaron_api
1 messages ยท Page 1 of 1 (latest)
๐ 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.
- aaron_code, 8 hours ago, 56 messages
- aaron_unexpected, 22 hours ago, 8 messages
Hey, this is related to the aaron_code thread from last night
We don't typically go back to previous threads, can you summarize the issue you're having here?
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
Can you give me the request ID for the request showing that error?
Looking...
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?
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?
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.
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
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.
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
Hhmmmm, I can see why you were thinking race condition then, like maybe two refresh requests went off too close to each other
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
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.
I appreciate that
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.
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
They're similar (there was no useful information on the discussion either, it was just two comments ๐ )
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?
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.
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
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.
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
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
Complete reference documentation for the Stripe API. Includes code snippets and examples for our Python, Java, PHP, Node.js, Go, Ruby, and .NET libraries.
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?
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.