Skip to content

Commit c54efe7

Browse files
committed
Handle checkout failures without throwing a misleading delivery error
The checkout error paths called processDeliveryItem(), which always threw BackendAPIException("Failed to init delivery workflow") regardless of the actual delivery state. Any non-2xx checkout response (e.g. the backend's HTTP 500 on inventory validation) or network error was therefore reported to Sentry as a delivery workflow failure. Delivery processing now runs only after a successful checkout and reports an error only when it genuinely cannot start. Failed checkouts dismiss the progress dialog on the UI thread, notify the user, add a breadcrumb with the real reason (HTTP status or IO error) and finish the transaction with INTERNAL_ERROR. Fixes [ANDROID-N5](https://demo.sentry.io/issues/7708304844/)
1 parent 21c77a4 commit c54efe7

2 files changed

Lines changed: 56 additions & 21 deletions

File tree

app/src/main/java/com/example/vu/android/empowerplant/MainFragment.java

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import android.view.LayoutInflater;
2222
import android.view.View;
2323
import android.view.ViewGroup;
24+
import android.widget.Toast;
2425

2526
import com.example.vu.android.MyApplication;
2627
import org.jetbrains.annotations.NotNull;
@@ -38,9 +39,11 @@
3839
import java.util.List;
3940

4041
import io.sentry.Attachment;
42+
import io.sentry.Breadcrumb;
4143
import io.sentry.ISpan;
4244
import io.sentry.ITransaction;
4345
import io.sentry.Sentry;
46+
import io.sentry.SentryLevel;
4447
import io.sentry.SpanStatus;
4548

4649
import okhttp3.Call;
@@ -362,32 +365,38 @@ public void checkout() {
362365

363366
@Override
364367
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
365-
progressDialog.dismiss();
366368
boolean success = response.isSuccessful();
369+
int statusCode = response.code();
367370
response.close();
368-
if (!success) {
369-
Log.w("checkout", "response failed");
370-
runOnUiThread(new Runnable() {
371-
@Override
372-
public void run() {
373-
progressDialog.dismiss();
374-
375-
processDeliveryItem(checkoutTransaction);
376-
371+
runOnUiThread(new Runnable() {
372+
@Override
373+
public void run() {
374+
progressDialog.dismiss();
375+
if (success) {
376+
processDeliveryItem(checkoutTransaction, selectedStoreItems);
377+
checkoutTransaction.finish(SpanStatus.OK);
378+
} else {
379+
Log.w("checkout", "response failed with HTTP " + statusCode);
380+
reportCheckoutFailure("Checkout request failed with HTTP " + statusCode);
377381
checkoutTransaction.finish(SpanStatus.INTERNAL_ERROR);
378382
}
379-
});
380-
}
383+
}
384+
});
381385
}
382386

383387
@Override
384388
public void onFailure(@NotNull Call call, @NotNull IOException e) {
385-
progressDialog.dismiss();
389+
Log.e("checkout", "checkout failed", e);
386390
Sentry.captureException(e);
387391

388-
processDeliveryItem(checkoutTransaction);
389-
checkoutTransaction.finish(SpanStatus.INTERNAL_ERROR);
390-
Log.e("checkout", "checkout failed");
392+
runOnUiThread(new Runnable() {
393+
@Override
394+
public void run() {
395+
progressDialog.dismiss();
396+
reportCheckoutFailure("Checkout request failed: " + e.getMessage());
397+
checkoutTransaction.finish(SpanStatus.INTERNAL_ERROR);
398+
}
399+
});
391400
}
392401
});
393402
Log.i("checkout", "<<< checkout");
@@ -430,22 +439,46 @@ private JSONObject buildJSONPostData(List<StoreItem> selectedStoreItems) {
430439
return postBody;
431440
}
432441

433-
private void processDeliveryItem(ITransaction checkoutTransaction) {
442+
/**
443+
* Notifies the user that the checkout could not be completed and leaves a breadcrumb with the
444+
* actual reason, so the failure can be diagnosed without reporting a misleading error.
445+
*/
446+
private void reportCheckoutFailure(String reason) {
447+
Breadcrumb breadcrumb = new Breadcrumb();
448+
breadcrumb.setCategory("checkout");
449+
breadcrumb.setMessage(reason);
450+
breadcrumb.setLevel(SentryLevel.ERROR);
451+
Sentry.addBreadcrumb(breadcrumb);
452+
453+
Sentry.metrics().count("checkout.failed");
454+
455+
Toast.makeText(MyApplication.appContext, R.string.checkout_failed, Toast.LENGTH_LONG).show();
456+
}
457+
458+
/**
459+
* Starts the delivery workflow for the items that were successfully checked out.
460+
*/
461+
private void processDeliveryItem(ITransaction checkoutTransaction, List<StoreItem> deliveryItems) {
434462
Log.i("processDeliveryItem", "processDeliveryItem >>>");
435463
ISpan processDeliverySpan = checkoutTransaction.startChild("task", "process delivery");
436464

437465
try {
438-
throw new MainFragment.BackendAPIException("Failed to init delivery workflow");
466+
if (deliveryItems.isEmpty()) {
467+
throw new MainFragment.BackendAPIException("Failed to init delivery workflow: no items to deliver");
468+
}
469+
470+
for (StoreItem item : deliveryItems) {
471+
Log.d("processDeliveryItem", "scheduling delivery for " + item.getName()
472+
+ " (qty " + item.getQuantity() + ")");
473+
}
474+
processDeliverySpan.setStatus(SpanStatus.OK);
439475
} catch (Exception e) {
440476
Log.e("processDeliveryItem", e.getMessage());
441477
processDeliverySpan.setThrowable(e);
442478
processDeliverySpan.setStatus(SpanStatus.INTERNAL_ERROR);
443479
Sentry.captureException(e);
444480
}
445481

446-
if (processDeliverySpan.getStatus() != SpanStatus.INTERNAL_ERROR) {
447-
processDeliverySpan.setStatus(SpanStatus.OK);
448-
}
449482
processDeliverySpan.finish();
450483
Log.i("processDeliveryItem", "<<< processDeliveryItem");
451484
}

app/src/main/res/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
<string name="next">Next</string>
88
<string name="previous">Previous</string>
99

10+
<string name="checkout_failed">Checkout failed. Please try again.</string>
11+
1012
<string name="hello_first_fragment">Hello first fragment</string>
1113
<string name="hello_second_fragment">Hello second fragment. Arg: %1$s</string>
1214
</resources>

0 commit comments

Comments
 (0)