stripe-refund-dispute-lifecycle

v2026.09.24

Complete Stripe refund and dispute lifecycle handling. PROACTIVELY activate for: (1) charge.refunded handler design, (2) charge.dispute.created / charge.dispute.closed handlers, (3) Refund delta computation from event.data.previous_attributes.amount_refunded, (4) Dispute-hold past_due status management, (5) shouldRestoreStatus predicate with satisfies Record<Stripe.Dispute.Status, boolean>, (6) Credit-pack vs subscription refund differentiation, (7) Checkout Session lookup for refund proportion math, (8) Allowlist default-deny for external enums, (9) stripe.checkout.sessions.list({payment_intent}) pattern, (10) Dispute outcome branch logic (won / lost / warning_closed / prevented). Provides: full handler patterns for all three events, predicate examples, credit-pack vs subscription math, exhaustive switch patterns.

GitHub
安装命令
npx skhub add josiahsiegel/stripe-refund-dispute-lifecycle
Markdown
SKILL.md

Quick Reference

EventActionKey rule
charge.refundedRevoke credits proportional to refund deltaG2 — previous_attributes.amount_refunded
charge.dispute.createdSet user past_due + store checkpointG1 + G9
charge.dispute.closedwon / warning_closed / prevented -> restore; lost -> no-op (charge.refunded handles); else -> no-opG5 + G7
Refund source priorityWhen to use
event.data.previous_attributes.amount_refunded (G2)Primary — always prefer
charge.refunds.data (sorted by created desc)Fallback when previous_attributes absent
stripe.refunds.list({charge, limit:1})Last resort when embedded missing
charge.amount_refunded aloneNEVER (cumulative, not per-event)

When to Use This Skill

Use when implementing any handler that revokes credits or mutates a paid-status column in response to Stripe refund or dispute events.

Related skills:

  • For getRefundDelta (the G2 delta helper): stripe-billing-master:stripe-list-pagination-previous-attributes
  • For the canonical refund helper and audit-row invariant: stripe-billing-master:stripe-credit-audit-trail
  • For the G1 checkpoint pattern every dispute handler wraps: stripe-billing-master:stripe-webhook-idempotency

Core Rules

G2: refund delta

Use getRefundDelta() from stripe-billing-master:stripe-list-pagination-previous-attributes — that skill owns the delta-computation helper. Key guarantee: the helper returns null when no source is available, and the handler MUST skip revocation rather than guess.

G7: exhaustive shouldRestoreStatus

const shouldRestoreMap = {
  won: true,
  warning_closed: true,
  prevented: true,
  lost: false,
  needs_response: false,
  under_review: false,
  warning_needs_response: false,
  warning_under_review: false,
  charge_refunded: false,
} satisfies Record<Stripe.Dispute.Status, boolean>;

export const shouldRestoreStatus = (s: Stripe.Dispute.Status): boolean => shouldRestoreMap[s];

When Stripe adds a new status in a future SDK version, this object is a compile error until you add the key — forcing a conscious G5 allowlist decision.

Credit-pack vs subscription refund

async function resolveCreditsToRevoke(charge: Stripe.Charge, refundAmount: number) {
  const sessions = await stripe.checkout.sessions.list({
    payment_intent: charge.payment_intent as string,
    limit: 1,
    expand: ["data.line_items"],
  });
  const session = sessions.data[0];
  if (session?.mode !== "subscription") {
    const pack = CREDIT_PACKS.find(p => session?.line_items?.data?.[0]?.price?.id === p.priceId);
    if (pack && session.amount_total && session.amount_total > 0) {
      // Proportional revocation: if they refunded 50% of the pack, revoke 50% of the credits
      return Math.round(pack.credits * (refundAmount / session.amount_total));
    }
  }
  // Subscription: 1 credit = 1 cent at cash-equivalent
  return refundAmount;
}

Notes on the credit-pack math: proportional revocation matters because packs are bulk-priced (e.g., 1000 credits for $9 instead of $10) — a flat refundAmount -> credits conversion over-revokes. Always look up the originating Checkout Session to distinguish mode: "subscription" (cash-equivalent) from mode: "payment" with a known pack price ID (proportional).

发现
标签

此技能尚未发布标签。

版本
最新版本元数据

版本

v2026.09.24

发布时间

2026年9月24日

分类

未分类

许可证

MIT

源路径

plugins/stripe-billing-master/skills/stripe-refund-dispute-lifecycle

默认分支

main

最新提交

5a1b112

Tree SHA

376c8e0