Recovering from Failures when Handling Payments with Stripe
In our previous post we explored webhooks as a reliable means to promptly respond to customer activities, such as checkouts, successful payments or failed transactions. Yet, in practice, we often deal with a variety of unforeseen errors that can present a threat to daily operations of an e-commerce platform. To mitigate service disruptions, it’s crucial to understand how Stripe communicates issues. In this post, we explore common Stripe exceptions and learn about managing network and connection issues.
This article is part of a comprehensive series. Be sure to check out the previous post, where you learn how to use and benefit from Stripe webhooks.
Disclaimer: I am not affiliated with Stripe. All insights shared in this article are based on my personal experience and opinions.
Table of Contents
- Stripe Error Anatomy
- Common Exceptions in Stripe’s Java Library
- Additional Exceptions to Be Aware of
- Managing Network and Connection Issues
- Summary
Stripe Error Anatomy
Stripe’s API uses standard HTTP status codes to indicate the success or failure of requests. In addition to these codes, it returns a JSON payload that contains more detailed information about the problem. Key elements included in the error response are:
- type: Represents the error type (e.g.
api_error
,card_error
etc) – see all error types here. - code: Helps understand the error (e.g
invalid_number
) - message: A descriptive message providing more details
When using Stripe’s Java library, errors are handled through exceptions. Each type of error thrown by the API can correspond to different exceptions in the Java library.
The Java SDK maps API errors to specific exceptions. Understanding this classification will help you make your payment system more robust and reliable.
Common Exceptions in Stripe’s Java Library
ApiException
Unexpected errors that occur server-side. These are rare but can happen during Stripe’s API downtime or unexpected issues on their end.
import com.stripe.exception.ApiException;
try {
// Stripe SDK call
} catch (ApiException e) {
logger.warn("Operation failed", e);
throw e;
}
ApiConnectionException
Represents issues related to network connectivity between your server and Stripe.
import com.stripe.exception.ApiConnectionException;
try {
// Stripe SDK call
} catch (ApiConnectionException e) {
logger.warn("Network error", e);
throw e;
}
AuthenticationException
Occur when your API key is incorrect or lacks the necessary permissions for the requested operation.
import com.stripe.exception.AuthenticationException;
try {
// Stripe SDK call
} catch (AuthenticationException e) {
logger.warn("Authentication with Stripe failed", e);
throw e;
}
CardException
This exception relates specifically to issues with card processing, such as declined payments or fraudulent flags.
import com.stripe.exception.CardException;
try {
// Stripe SDK call
} catch (CardException e) {
logger.warn("There was an issue with the card", e);
throw e;
}
InvalidRequestException
Happens due to invalid parameters or resource IDs in your request.
import com.stripe.exception.InvalidRequestException;
try {
// Stripe SDK call
} catch (InvalidRequestException e) {
logger.warn("Invalid request", e);
throw e;
}
RateLimitException
Occurs when too many requests are made to the API in a short amount of time.
import com.stripe.exception.RateLimitException;
try {
// Stripe SDK call
} catch (RateLimitException e) {
logger.warn("Too Many Requests", e);
throw e;
}
Additional Exceptions to Be Aware of
- ApiKeyMissingException: This exception is thrown when you forget to initialize the SDK with your API key. Typically, you want to set the key globally through
Stripe.apiKey = "YOUR_SECRET_API_KEY";
. - SignatureVerificationException: You will likely encounter this one when working with webhooks. Essentially, each webhook request is signed and the signature is subject to verification.
- IdempotencyException: Indicates that the request did not adhere to idempotent rules. For instance, if the parameters changed between requests with the same idempotent key. In general, idempotency is an important feature that let’s you retry operations that would otherwise result in data inconsistencies. Curious to learn more? Check this post.
Managing Network and Connection Issues
Network and connection issues can occur for various reasons, such as timeouts or intermittent server availability. Merely catching ApiConnectionException
might not be enough. You want to implement a retry mechanism for handling transient errors that are likely to resolve on their own after a short period.
Stripe supports idempotent requests for safely retrying transactions without accidentally performing the same operation twice. This is done by adding an Idempotency-Key
header to your requests.
import com.stripe.model.Charge;
import com.stripe.param.ChargeCreateParams;
import com.stripe.net.RequestOptions;
RequestOptions options = RequestOptions.builder()
.setIdempotencyKey("unique-key")
.build();
Charge charge = Charge.create(
ChargeCreateParams.builder()
.setAmount(1000L)
.setCurrency("usd")
.setSource("tok_visa")
.build(),
options
);
Next, make sure to change timeout setting for your HTTP client whenever the default settings are insufficient. Stripe’s Java library exposes the relevant keys:
// Set the connection timeout to 2 seconds
Stripe.setConnectTimeout(2000);
// Set the API read timeout to 5 seconds
Stripe.setReadTimeout(5000);
Summary
Effectively managing network and connection issues and implementing resilient coding practices are essential for a reliable integration with Stripe’s APIs. Utilizing idempotency for safe retries with a robust mechanism such as exponential backoff and customizing timeout settings can significantly enhance your application’s robustness. Always refer to Stripe’s latest documentation and best practices to keep your integration up to date.
Thanks for reading and stay tuned for the next article where we expand on dealing with edge cases. In particular, we will implement handling of declined payments and provide a customer-friendly way of retrying a payment.