Handling errors
Orders can fail to place for a variety of different reasons. In this guide, we’ll walk through how to handle various failure modes. Some examples of issues which can occur while checking out are:
- Deliverability. Most merchants only deliver to certain parts of the world. If your buyer’s identity is outside of the regions where the merchant ships, then we won’t be able to fulfill your order.
- External service failures. The Rye API builds on top of many other systems. Occassionally one of these services will fail, and this bubbles up through our API.
- Input validation. Rye reflects the validation requirements of the store(s) your cart holds products from. Some merchants require that the second address line is provided, for instance.
- Merchant configuration. Rye attempts to support any store on the internet, but some platforms—notably Shopify—are extremely customizable and not all setups are supported by us.
- Payment errors. Payment processing may fail for reasons such as insufficient funds, unrecognized payment methods, or declined transactions.
- Product inventory. Inventory availability is verified before checkout, but items in high demand can go out of stock during order placement.
Overview
Placing an order consists of two phases.
- Cart submission (synchronous). When you make a request to
submitCart
we run some checks against theCart
to try and verify whether it can be successfully purchased, and if you passed us a payment method we’ll charge it before moving to the next phase. - Order placement (asynchronous). After
submitCart
returns, your order is placed on a queue and is processed asynchronously. You must poll thecheckoutByCartID
query for updates. Sometimes things go wrong at this stage and you’ll need to replace the order.
If either of these phases fail, our system will try to determine the retryability of the operation that failed. Cart submission returns this information via a GraphQL extension field named retryable
, and order placement attaches this information to the OrderFailedOrderEvent
object. In both cases, retryable
is an optional boolean field which you can interpret like so:
- A
true
value means our system believes the error is intermittent, and you can safely retry the operation. - A
false
value means the failure either isn’t intermittent (and thus won’t be fixed through retries), or that retrying the operation is unsafe (e.g. there is a risk of charging twice). - A missing value means our system didn’t have enough information to make a judgement about retryability. You should investigate these cases yourself.
Guidance by phase
Cart submission (synchronous)
Cart submission is a synchronous process where Rye verifies that it is possible for us to place your order. Our systems perform tasks like address validation and product inventory checks to make sure that the order will go through if we promote it to the order placement phase.
Most errors occurring in this phase are safe to retry. The very last step taken by our systems in this phase is to capture payment from the provided payment token (if one is present), and immediately after that the cart gets sent to the next phase.
If you do not receive a retryable
extension with value true
, then that generally means the cart submission error needs to be fixed by either altering your request or coordinating with the merchant. For instance, the following response indicates that cart submission cannot be retried due to how the merchant has configured their store:
Here are some example failures:
- A payment error (
PAYMENT_FAILED
) usually requires a change of payment method to resolve. You could prompt your shopper to key in a different credit card. Because we don’t expect payment method errors to solve themselves with a retry, we’ll send backretryable: false
. - A deliverability error (
UNDELIVERABLE
) means that the merchant can’t ship the order to the address in the buyer identity. You could prompt your shopper to enter a different delivery address. We’ll send backretryable: false
in this case. - A product inventory error (
PRODUCTS_UNAVAILABLE
) can sometimes resolve itself if the merchant happens to restock the product in between cart submission attempts. We’ll send backretryable: true
in this case. - An upstream service error (
UPSTREAM_SERVICE_ERROR
) is generally transient, and we’ll usually send backretryable: true
.
Caveat: Request timeouts
Calling submitCart
involves making a synchronous request to our API. If you have configured a low request timeout—or our systems exceed our own internal timeout—you’ll get back an HTTP 503 or HTTP 504 status code. In this case, it is entirely possible that your cart ended up successfully moving to the order placement phase in the background.
For this reason, you should not immediately retry request timeouts to submitCart
. You must poll the checkoutByCartID
query to verify what ended up happening to the cart after your request timed out. See the next section for specific guidance on this.
Order placement (asynchronous)
After cart submission completes, the Rye API attempts to actually pay for the items inside the cart. We do this by creating orders with the merchant(s) that sell the items you’re trying to purchase. If you’re purchasing two items that are each sold by different merchants, then we’ll end up creating two orders in total from your cart.
Most orders which get to this point will ultimately be successful, but success is not guaranteed. As Rye needs to talk to external systems to place the order, this phase is particularly vulnerable to failures caused by upstream service outages.
You should poll the checkoutByCartID
query for updates on your order. Progress on each order is communicated via OrderEvent
objects stored against each Order
object on the Checkout
.
At a high level, the lifecycle of a single order looks like this:
You can poll for updates using the following query:
Here’s an example payload showing an order that failed with a retryable error:
There are a few cases you should look out for here:
- The order never starts processing. This could be the case if your call to
submitCart
timed out, and something prevented our system from moving it to the order placement phase. You can detect this by theevents
list being empty.- We recommend polling for 5 minutes before declaring that the order didn’t start processing.
- In this case, it is safe to call
submitCart
on the original cart to retry submission.
- The order gets stuck during processing. You can detect this by the
events
list containing only a single event of typeOrderSubmissionStartedOrderEvent
.- We recommend polling for 10 minutes before handling this edge case.
- This case is rare and indicates something went wrong on our side
- You should not automatically attempt to replace orders which fall into this bucket. Contact us at dev@rye.com with the cart ID so we can help you debug the issue.
- The order fails to place. You can detect this by the presence of an
OrderFailedOrderEvent
event inside theevents
list.- You can use the
OrderFailedOrderEvent.retryable
field to determine whether you can automatically attempt to replace the order. The value of this field has the same meaning as theretryable
extension.
- You can use the
In cases (2) and (3), it is not possible to retry the order by calling submitCart
on the original Cart
. Our systems prevent this, and you will receive a non-retryable ALREADY_SUBMITTED
error code from the submitCart
operation.
Instead, you must create a new cart containing the item(s) from the order(s) that failed to place, and then attempt to submit the new Cart
.
Refunds
Rye automatically issues refunds to the payment method token passed to submitCart
when order placement fails. If you are an enterprise customer using invoice billing (and therefore do not pass us a payment method token), then the failed order will not be charged on your next invoice.
If you are an enterprise customer using invoice billing, then you may need to refund your shopper depending on how your app is structured.
Sample code
The below is an example of how you can implement handling ordering errors with retries in TypeScript. We use the async-retry
NPM package to implement retries for the submitCart
operation using exponential backoff.
Was this page helpful?