{"record":{"id":"4c23a459f30e7643","repo":"antiwork/gumroad","slug":"something-went-wrong-4c23a4","errorCode":null,"errorMessage":"Something went wrong.","messagePattern":"Something went wrong\\.","errorType":"exception","errorClass":"ResponseError","httpStatus":null,"severity":"error","filePath":"app/javascript/data/purchase.ts","lineNumber":463,"sourceCode":"const confirmPaymentAfterAction = async ({\n  purchaseId,\n  clientSecret,\n  stripeError,\n}: {\n  purchaseId: string;\n  clientSecret: string;\n  stripeError: StripeError | undefined;\n}): Promise<LineItemResult> => {\n  const response = await request({\n    method: \"POST\",\n    url: Routes.confirm_purchase_path(purchaseId),\n    accept: \"json\",\n    data: {\n      client_secret: clientSecret,\n      stripe_error: stripeError,\n    },\n  });\n  if (!response.ok) throw new ResponseError();\n  return typia.assert<LineItemResult>(await response.json());\n};\n","sourceCodeStart":445,"sourceCodeEnd":466,"githubUrl":"https://github.com/antiwork/gumroad/blob/afeacbd394069a1cbf0c6c50ee8e900925050370/app/javascript/data/purchase.ts#L445-L466","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect the confirm_purchase POST in DevTools: the 4xx body states whether the refusal is secret mismatch, purchase state, or validation","Confirm clientSecret was minted for this purchaseId's PaymentIntent in the current session; regenerate and retry when stale","If the purchase already progressed (succeeded or webhook-handled), route to the success/receipt state rather than erroring","Pass the StripeError object through when Stripe.js itself failed so the server can record and reason about it","If 401s cluster, check session expiry mid-checkout and restore the session before re-confirming"],"exampleFix":"// before\nif (!response.ok) throw new ResponseError();\nreturn typia.assert<LineItemResult>(await response.json());\n\n// after\nif (!response.ok) {\n  const body = await response.json().catch(() => null) as { error?: string } | null;\n  throw new ResponseError(body?.error ?? `Confirm failed (${response.status})`);\n}\nreturn typia.assert<LineItemResult>(await response.json());","handlingStrategy":"try-catch","validationCode":"const confirmable = (purchaseId: string, clientSecret: string): boolean =>\n  purchaseId.length > 0 && clientSecret.length > 0;","typeGuard":"import { ResponseError, RateLimitError, assertResponseError } from '$app/utils/request';\nif (e instanceof RateLimitError) { /* wait e.retryAfter */ } else { assertResponseError(e); }","tryCatchPattern":"try {\n  const result = await confirmPurchase(purchaseId, { clientSecret, stripeError });\n} catch (e) {\n  assertResponseError(e);\n  // generic message means a 4xx body was dropped — check purchase state before offering retry\n  showError('Payment could not be confirmed. Please try again.');\n}","preventionTips":["Pair clientSecret with the purchaseId that minted it, in the same session","Route already-succeeded purchases to the receipt instead of erroring","Pass stripeError through whenever Stripe.js fails so the server can record it","Disable the pay button while confirm is in flight"],"tags":["stripe","checkout","http-4xx","purchase"],"backgroundTag":"http-4xx-client-error","analyzedSha":"afeacbd394069a1cbf0c6c50ee8e900925050370","analyzedAt":"2026-08-21T17:58:52.159Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}