stripe-refund-dispute-lifecycle について
Stripe の返金・異議申し立て Webhook に応じて、クレジットや支払い状態を変更する処理を実装する開発者向けスキルです。返金差額の算出、異議ステータスの明示的な判定、サブスクリプションとクレジットパックの返金区別を扱います。
主な機能
- 01返金差額の情報源の優先順位
- 02異議ステータスの復元許可リスト
- 03クレジットパックの比例失効
- 04サブスクリプションと支払いモードの区別
活用シーン
- 01Stripe 返金 Webhook の実装
- 02異議申し立ての開始と終了への対応
- 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.closed | won / 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 クレジットが $10 ではなく $9)、返金に比例した取り消しが重要です。refundAmount -> credits の一律換算では、取り消しすぎになります。必ず元の Checkout Session を参照し、mode: "subscription"(現金相当)と、既知のパック価格 ID を持つ mode: "payment" を区別してください。