retrying idempotent requests and error responses from stripe

已查看 1,755 次
跳至第一个未读帖子

Luke Valenty

未读,
2015年8月14日 12:56:412015/8/14
收件人 Stripe API Discussion
Hi everyone, 

I need some sage advice on retrying failed requests and in what cases (if any) I will need to retry a failed request with a new idempotency key.

First I need to know exactly what types of responses are cached for a request with an idempotency key.  The official API reference says the same response is always sent back without mentioning exceptional conditions (https://stripe.com/docs/api/java#idempotent_requests):

"We'll always send back the same response for requests made with the same key, even if you make the request with different request parameters."

If this is the case then I will always want to use a new idempotency key if I get an error from stripe that can be retried.  On the other hand, maybe it's possible some internal stripe server errors should be retried with an idempotent key.  It would be nice for the API reference to explicitly state how error responses are handled with idempotency keys.

Additionally, how unique do the idempotency keys need to be?  The API reference suggests using random strings or UUIDs.  I think it is reasonable for them to be unique per apiKey, but the API reference does not explicitly state this.

Assuming that error responses from stripe don't change the behavior of the idempotency key, here's a list of the various error conditions and how I think they should be handled:
  1. 400 - Bad Request
    1. My code or the Java API has a bug and when it is fixed the request should be re-issued with a new idempotency key
  2. 401 - Unauthorized
    1. My code or the Java API has a bug and when it is fixed the request should be re-issued with a new idempotency key
  3. 402 - Request Failed 
    1. invalid_number, invalid_expiry_month, invalid_expiry_year, invalid_cvc, incorrect_number, expired_card, incorrect_cvc, incorrect_zip, missing
      1. These card errors require intervention from the customer, they require a new credit card or the order to be canceled
      2. If a new credit card is added that card can be charged with stripe using a new idempotency key.
    2. card_declined
      1. Card might have insufficient funds, it's a new card that's not yet activated, or maybe it's put on hold due to the issuers fraud prevention (that's happened to me a few times after coming back from vacation).  Maybe there are a few more reasons why this can be, but I don't think it matters too much because it doesn't look like stripe will/can tell you which one it is.
      2. If the card is declined there are four possible ways to resolve it: retry the charge later, ask for a new card, change to cash payment, cancel the order. 
      3. I think I will end up sending the customer an email letting them know their card was declined and that we will retry charging shortly if they take no action.  I will give them the option of adding a new card, changing to cash, or canceling the order.
      4. If/when the charge is retried, it will be retried with a new idempotency key.
    3. processing_error
      1. I'm not sure what this means, maybe stripe's servers received an error from whatever other service they need to talk to to actually process a card.  Then I would assume that the 500 series response codes are dedicated purely to stripe server errors.
      2. I don't know if this would be a transient issue or not.  I might have to treat it similarly to how card_declined is treated, but maybe I could retry a few times before emailing the customer about the issue.  I could retry a couple times immediately, and maybe retry over a longer period of time, hours or days.
  4. 404 - Not Found
    1. Either my code has a bug, or a connected account has had a customer or card deleted using the Stripe web page and not over my platform.  This is something I hadn't considered until just now.  
    2. In order to handle this I will need to email both the customer and the owner of the connected account and let them know what happened and how it might have been caused (and don't do that in the future!).  I will then need to initiate the flow to get a new credit card from the customer.
  5. 500, 502, 503, 504 - Server Errors
    1. Stripe's servers are foobar and the request will be retried with a new idempotency key immediately (within seconds or milliseconds with randomized exponential back off) and over a longer period of time (hours or days) if needed.
    2. What do the different numbers mean?  Is it ever possible for a charge to go through with a 50X server error response code?
  6. SSLHandshakeException, SocketTimeoutException, IOException
    1. Network connection is unreliable, request will be retried with same idempotency key immediately (within seconds or milliseconds with randomized exponential back off) and over a longer period of time (hours or days) if needed.
  7. Google Cloud Datastore Exceptions
    1. If my process can't write back the response from stripe's servers due to a datastore issue, then the same request will be retried using the same idempotency key both immediately and at a later date if needed.  From my code's perspective when I retry at a later date, it will appear as if the initial request never happened.
    2. Datastore issues should be transient on the scale of milliseconds, but there have been longer outages lasting hours or more.  If it's been longer than 24 hours since the original time the charge should have been made then I will also need to fetch a list of charges for the given customer within this date range and look for the idempotent key I also put in the metadata.
  8. Any other Java Exception (besides stripe java api exceptions that are already covered by status codes above)
    1. Bug in my code, needs to be fixed and retried with same idempotency key.
I think this covers all of the types of errors that can occur.  I would love if something with more experience could provide some feedback to me about these flows and point out any potential issues I have here.  This is the first time I've developed a billing system and it's a bit daunting.

-Luke


Matthew Arkin

未读,
2015年8月15日 10:23:532015/8/15
收件人 Jake K.
The only thing idempotency tokens is for is the case where you don't actually get a response from Stripe's servers (so client side timeouts). In any case where you do get a response, using the same idempotency token within 24 hours should give you the same response, no matter the type. 

It should be unique per api key. 

--
You received this message because you are subscribed to the Google Groups "Stripe API Discussion" group.
To post to this group, send email to api-d...@lists.stripe.com.
Visit this group at http://groups.google.com/a/lists.stripe.com/group/api-discuss/.

To unsubscribe from this group and stop receiving emails from it, send an email to api-discuss...@lists.stripe.com.

Pradip SS

未读,
2019年7月9日 14:03:022019/7/9
收件人 Stripe API Discussion、mar...@kollective.it
I am also struggling with same scenarios, i need some documentation from which i decide should i have to generate new idempotency key or use same idempotency key before retry (i am using Polly for same)
Example where i need to decide it.
When i try to create subscription for customer and it has already 25 subscription then i get error code from stripe customer_max_subscriptions, so i delete all current subscription and move to retry (using same polly cycle where on 1st try i get customer_max_subscriptions error), in this case i got idempotency_error error code.

So we have need some specification from which we can decide need to generate new idempotency key or can use same idempotency key  
To unsubscribe from this group and stop receiving emails from it, send an email to api-d...@lists.stripe.com.

Remi J.

未读,
2019年7月9日 20:40:412019/7/9
收件人 api-d...@lists.stripe.com
Hello!

Idempotent requests are used to safely retry requests without accidentally performing the same operation twice. This means that if you create a charge and then there's a connection error and you never get the response, you should run the request again with the same key. If we had received your original request, we will return the same information (and same charge) and if we hadn't we would create a Charge. This ensures you don't charge your customer twice by mistake.

All other cases usually require that you create a new idempotency key. If you try to create a Subscription and get an error that the Customer already has 25 Subscriptions, you need a new key. If you don't send a new key, when you run the request again, you'll get the same error because the result is cached. Even if you have deleted the other subscriptions since then.

This is true for all errors. If the card is declined and we return a card error and a 402 status, retrying with the same key would yield the same cached error. We would not talk to the bank again. You need a new key to try the card again.

Hope this helps!
Remi

--
To unsubscribe from this group and stop receiving emails from it, send an email to api-discuss...@lists.stripe.com.

Pradip SS

未读,
2019年7月10日 13:56:342019/7/10
收件人 Stripe API Discussion
Thanks for clarification, so basically when I get any response from stripe (success or error) and after that if i need to retry (in case of error) then i need new idempotency key.
And any case where i don't have response (success or fail) from stripe then i can retry using same idempotency key.

Thanks Remi.
To unsubscribe from this group and stop receiving emails from it, send an email to api-d...@lists.stripe.com.

Remi J.

未读,
2019年7月10日 13:59:382019/7/10
收件人 api-d...@lists.stripe.com
Correct. If you get no response or an error of type connection_error then you should retry with the same idempotency key. If you get an error back then you'll need a new key. The tricky part is when you get a 5xx error back which happens if something went wrong. In that case you need a new key, but it's still possible for the original request to have had side effects (such as creating a charge). In that case, you'll need to listen to events on your webhook handler to catch objects created that you did not receive in our API (because you got an error).

To unsubscribe from this group and stop receiving emails from it, send an email to api-discuss...@lists.stripe.com.
回复全部
回复作者
转发
0 个新帖子