antiwork/gumroad · error · ResponseError

Something went wrong.

Error message

Something went wrong.

What it means

ResponseError ('Something went wrong.') thrown at purchase.ts:463 when the POST to Routes.confirm_purchase_path(purchaseId) — the single-purchase counterpart of the cart flow, sending client_secret and stripe_error — returns non-ok. As everywhere in this layer, request() has already converted 5xx, 429 and network failures, so this throw is a 4xx: the server received the confirm request for this one purchase and refused it, and the status/body detail is dropped in favor of the default message.

Source

Thrown at app/javascript/data/purchase.ts:463

const confirmPaymentAfterAction = async ({
  purchaseId,
  clientSecret,
  stripeError,
}: {
  purchaseId: string;
  clientSecret: string;
  stripeError: StripeError | undefined;
}): Promise<LineItemResult> => {
  const response = await request({
    method: "POST",
    url: Routes.confirm_purchase_path(purchaseId),
    accept: "json",
    data: {
      client_secret: clientSecret,
      stripe_error: stripeError,
    },
  });
  if (!response.ok) throw new ResponseError();
  return typia.assert<LineItemResult>(await response.json());
};

View on GitHub (pinned to afeacbd394)

Solutions

  1. Inspect the confirm_purchase POST in DevTools: the 4xx body states whether the refusal is secret mismatch, purchase state, or validation
  2. Confirm clientSecret was minted for this purchaseId's PaymentIntent in the current session; regenerate and retry when stale
  3. If the purchase already progressed (succeeded or webhook-handled), route to the success/receipt state rather than erroring
  4. Pass the StripeError object through when Stripe.js itself failed so the server can record and reason about it
  5. If 401s cluster, check session expiry mid-checkout and restore the session before re-confirming

Example fix

// before
if (!response.ok) throw new ResponseError();
return typia.assert<LineItemResult>(await response.json());

// after
if (!response.ok) {
  const body = await response.json().catch(() => null) as { error?: string } | null;
  throw new ResponseError(body?.error ?? `Confirm failed (${response.status})`);
}
return typia.assert<LineItemResult>(await response.json());
Defensive patterns

Strategy: try-catch

Validate before calling

const confirmable = (purchaseId: string, clientSecret: string): boolean =>
  purchaseId.length > 0 && clientSecret.length > 0;

Type guard

import { ResponseError, RateLimitError, assertResponseError } from '$app/utils/request';
if (e instanceof RateLimitError) { /* wait e.retryAfter */ } else { assertResponseError(e); }

Try / catch

try {
  const result = await confirmPurchase(purchaseId, { clientSecret, stripeError });
} catch (e) {
  assertResponseError(e);
  // generic message means a 4xx body was dropped — check purchase state before offering retry
  showError('Payment could not be confirmed. Please try again.');
}

Prevention

When it happens

Trigger: 403/404 when the client_secret doesn't match the purchase's PaymentIntent or the purchaseId is unknown; 409/422 when the purchase already succeeded (webhook confirmed it first), was canceled or expired, or the payment requires an action the server doesn't consider confirmable; 401 when the session expired mid-checkout.

Common situations: Webhook confirming the PaymentIntent before the browser's confirm call lands (race after slow network); double-click of the pay button firing two confirms; checkout resumed from a saved tab whose clientSecret belongs to a superseded PaymentIntent; dev setups where Stripe test-mode keys and the connect account disagree.

Related errors


AI-assisted analysis of antiwork/gumroad@afeacbd394 (2026-08-21). Data as JSON: /api/errors/4c23a459f30e7643. Report an issue: GitHub.