{"record":{"id":"a9d8fa7d7faa0962","repo":"sickn33/agentic-awesome-skills","slug":"payment-failed-a9d8fa","errorCode":null,"errorMessage":"Payment failed","messagePattern":"Payment failed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"skills/fp-pragmatic/SKILL.md","lineNumber":509,"sourceCode":"        : E.left('Valid age required')\n    ),\n    E.map(({ email, age }) => ({ email, age }))\n  )\n```\n\n### Promise Chain to TaskEither\n\n```typescript\n// Before\nasync function processOrder(orderId: string): Promise<Receipt> {\n  const order = await fetchOrder(orderId)\n  if (!order) throw new Error('Order not found')\n\n  const validated = await validateOrder(order)\n  if (!validated.success) throw new Error(validated.error)\n\n  const payment = await processPayment(validated.order)\n  if (!payment.success) throw new Error('Payment failed')\n\n  return generateReceipt(payment)\n}\n\n// After\nconst processOrder = (orderId: string): TE.TaskEither<string, Receipt> =>\n  pipe(\n    fetchOrderTE(orderId),\n    TE.flatMap(order =>\n      order ? TE.right(order) : TE.left('Order not found')\n    ),\n    TE.flatMap(validateOrderTE),\n    TE.flatMap(processPaymentTE),\n    TE.map(generateReceipt)\n  )\n```\n\n---","sourceCodeStart":491,"sourceCodeEnd":527,"githubUrl":"https://github.com/sickn33/agentic-awesome-skills/blob/58d857988fcfac6986206bca2b2fe223aa437e4b/skills/fp-pragmatic/SKILL.md#L491-L527","documentation":"Thrown in the fp-pragmatic 'Before' example when processPayment returns a result object with success falsy — the payment gateway or charge step declined the order. The skill replaces this control flow with a TaskEither chain where payment failure becomes a left value.","triggerScenarios":"Calling processOrder where validateOrder succeeds but processPayment resolves to { success: false, ... } — insufficient funds, gateway timeout mapped to failure, or a declined test card.","commonSituations":"Sandbox payment gateways returning declines for specific test card numbers; expired customer payment tokens; amount/currency mismatches between your validation and the gateway.","solutions":["Log and inspect payment.success payload/error code from the gateway response before this throw fires","Retry idempotently for transient gateway failures (timeouts, 5xx) and only fail on definitive declines","Model the step as TaskEither and use TE.filterOrElse on the payment result so failure flows as data"],"exampleFix":"// before\nconst payment = await processPayment(validated.order)\nif (!payment.success) throw new Error('Payment failed')\n\n// after\nconst pay = (o: Order) =>\n  TE.filterOrElse(\n    TE.tryCatch(() => processPayment(o), E.toError),\n    (p) => p.success,\n    () => new Error('Payment failed')\n  )","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await processOrder(orderId)\n} catch (e) {\n  if (e instanceof Error && e.message === 'Payment failed') {\n    // inspect gateway reason; retry only transient causes idempotently\n  }\n  throw e\n}","preventionTips":["Make payment calls idempotent with idempotency keys before enabling retries","Classify gateway error codes into transient vs definitive declines"],"tags":["payment","async","business-rule","fp-ts"],"backgroundTag":"payment-declined","analyzedSha":"58d857988fcfac6986206bca2b2fe223aa437e4b","analyzedAt":"2026-08-26T11:55:59.350Z","schemaVersion":2},"datasetVersion":"2026-08-26T14:46:13.012Z"}