mcpField
S
stripe-refund-dispute-lifecycle

stripe-refund-dispute-lifecycle

github.com/josiahsiegel开发5979 次安装19 周前更新
代码仓库

关于 stripe-refund-dispute-lifecycle

面向开发者的技能,用于实现会调整积分或付费状态的 Stripe 退款与争议 Webhook 处理程序。它强调退款差额计算、明确处理争议状态,并区分订阅退款与积分包退款。

主要功能

  1. 01退款差额来源优先级
  2. 02争议状态恢复白名单
  3. 03按比例撤销积分包积分
  4. 04区分订阅与支付模式

适用场景

  1. 01实现 Stripe 退款 Webhook 处理程序
  2. 02处理争议创建与关闭
  3. 03退款后按比例撤销积分

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/josiahsiegel/claude-plugin-marketplace --skill stripe-refund-dispute-lifecycle

安装后在 Claude Code 中输入/stripe-refund-dispute-lifecycle确认已加载。

详细介绍

快速参考

事件操作关键规则
charge.refunded按退款增量比例撤销积分G2 — previous_attributes.amount_refunded
charge.dispute.created将用户设为 past_due,并存储检查点G1 + G9
charge.dispute.closedwon / warning_closed / prevented -> 恢复;lost -> 无操作(由 charge.refunded 处理);其他情况 -> 无操作G5 + G7
退款来源优先级使用时机
event.data.previous_attributes.amount_refunded(G2)首选 — 始终优先使用
charge.refunds.data(按 created 降序排列)previous_attributes 缺失时的回退方案
stripe.refunds.list({charge, limit:1})内嵌数据缺失时的最后手段
仅使用 charge.amount_refunded绝不使用(这是累计值,并非单次事件的值)

何时使用此技能

实现任何用于响应 Stripe 退款或争议事件而撤销积分或修改付费状态列的处理程序时,请使用此技能。

相关技能:

  • 关于 getRefundDelta(G2 增量辅助函数):stripe-billing-master:stripe-list-pagination-previous-attributes
  • 关于规范的退款辅助函数和审计行不变量:stripe-billing-master:stripe-credit-audit-trail
  • 关于每个争议处理程序都会封装的 G1 检查点模式:stripe-billing-master:stripe-webhook-idempotency

核心规则

G2:退款增量

使用 stripe-billing-master:stripe-list-pagination-previous-attributes 中的 getRefundDelta() — 该技能负责增量计算辅助函数。关键保证:当没有可用来源时,辅助函数会返回 null,处理程序必须跳过撤销操作,而不是猜测。

G7:穷尽式 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];

当 Stripe 在未来的 SDK 版本中新增状态时,在添加对应键之前,此对象会导致编译错误 — 这会强制你审慎决定是否将其加入 G5 允许列表。

积分包与订阅退款
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;
}

关于积分包计算的说明:由于积分包采用批量定价(例如,1000 积分售价 $9,而不是 $10),因此按固定比例将 refundAmount 转换为积分会导致撤销过多。务必查找发起交易的 Checkout Session,以区分 mode: "subscription"(等值现金)与具有已知积分包价格 ID 的 mode: "payment"(按比例计算)。

评论

支持 Markdown