---
title: "Stripeの支払い失敗アラート：チャージバック、決済失敗、死んだWebhook"
description: "Stripeはチャージバックや支払い失敗をメールで知らせます。Stripeのwebhookイベントをプッシュ、時間指定通知、電話に変える方法を、具体的なイベントタイプ・条件・テンプレートとともに解説します。"
date: 2026-09-11
author: Nooc
authorAvatarLink: /images/avatars/nooc.webp
authorLink: https://nooc.me
tags:
  - Stripe
  - 決済
  - Webhook
  - チャージバック
  - SaaS
  - アラート
---

# Stripeの支払い失敗アラート：チャージバック、決済失敗、死んだWebhook

チャージバックが起きたこと、サブスクリプションの決済が失敗したこと、入金が戻されたこと——Stripeはすべて把握していて、メールで知らせてくれます。もっと大きな音が必要なら、Stripeにもう一つwebhookエンドポイントを登録し、その宛先をEchobellのチャンネルURLにして、ごく少数のイベントタイプだけを購読し、それぞれに付いている「締め切り」に合わせて通知タイプを選びます。チャージバックと早期不正警告にはタイマーが動いています。更新の決済失敗には、たいてい動いていません。

この記事では、どのStripeイベントが割り込みに値するか、5分ほどでStripeをEchobellにつなぐ方法、各オブジェクトに実在するテンプレートフィールド、そして署名検証を省くことで受け入れるトレードオフを扱います。

## 本当に割り込みに値するイベント

決済アラートの失敗パターンはいつも同じです。気分がいいので `payment_intent.succeeded` を購読し、スマホが1日40回震え、6週間後にチャージバックの通知が未読のまま流れていく。出発点を締め切りに変えましょう。8時間気づかなくても何のコストも生まないなら、それは8秒で届く必要がありません。

| イベント | なぜ重要か | 推奨タイプ |
| --- | --- | --- |
| `charge.dispute.created` | 応答できる期間は限られています——[通常はカードネットワークにより7〜21日](https://docs.stripe.com/disputes/responding)。逃せば自動的に敗北です。 | 電話 |
| `radar.early_fraud_warning.created` | カード発行会社が、その決済は不正かもしれないとStripeに通知した状態です。正式なチャージバックになる前の返金がまだ取れる手であり、その窓は短いです。 | 電話 |
| `payout.failed` | Stripeが集めたお金があなたの銀行に届きません。給与支払いも資金繰りの計算も、いま全部ずれています。 | 電話 |
| `invoice.payment_failed` | 非自発的チャーン。自然に解消することも多いので電話はやりすぎですが、金額の大きいアカウントはその日のうちに見る価値があります。 | 時間指定 |
| `customer.subscription.deleted` | 自発的チャーン。今日知れば十分で、起こされるほどではありません。 | 通常 |
| `payment_intent.succeeded` | 何も壊れていません。他の5つを無視する習慣を作るのが、まさにこのイベントです。 | 通知しない |

これらの段階はEchobellの3つの[通知タイプ](/ja/docs/notification)に対応します。**通常**は普通のプッシュ、**時間指定**はほとんどの集中モードを突破し、**電話**は着信として表示されるためおやすみモードでも鳴ります。購読者はチャンネルごとに自分のレベルを選べるので、共同創業者はチャージバックを電話で、サポート担当は同じものをプッシュで受け取れます。

## 必要なもの

- [WorkbenchのWebhooksタブ](https://dashboard.stripe.com/webhooks)にアクセスできるStripeアカウント
- Echobellのインストール（[App Store](https://apps.apple.com/app/apple-store/id6743597198?pt=128151925&ct=blog-stripe-payment-failure-alerts-ja&mt=8) / [Google Play](https://play.google.com/store/apps/details?id=one.echobell.echobellandroid)）
- 5分。サーバーもデプロイもコードも不要です——署名検証が欲しい場合を除いて。それは最後の節で扱います。

## ステップ1 — イベントタイプごとに1チャンネル

「Stripe」という1つのチャンネルを作って全部そこに流したくなります。やめましょう。チャージバック、請求書、入金はそれぞれ違うオブジェクトを運ぶので、本文テンプレートもダッシュボードのリンクも別物になります——そして、この構成の目的はまさに、何も開かずに通知だけで何が起きたか分かることにあります。

イベント名でチャンネルを作ります：`Stripe Disputes`。タイトルと本文のテンプレートは、ロック画面で読み切れる形に：

```
Title: 🔴 Dispute opened — {{data.object.reason}}
Body: Amount: {{data.object.amount}} {{data.object.currency}}
Charge: {{data.object.charge}}
Status: {{data.object.status}}
```

詳細設定でリンクテンプレートを設定し、通知レコードから正しいページが開くようにします：

```
https://dashboard.stripe.com/disputes/{{data.object.id}}
```

そして自分を**電話**タイプで購読し、チャンネル詳細画面からwebhook URLをコピーします。`https://hook.echobell.one/t/<channel-token>` のような形です。

<Callout type="info">チャンネルの詳細設定で **POST Only** をオンにしてください。Stripeは常にPOSTで送信するため、このスイッチによって、URLをチャットやブラウザのタブに貼っても偽のチャージバック通知が発火しなくなります。</Callout>

## ステップ2 — Stripeをそのチャンネルに向ける

Stripeダッシュボードで[Webhooksタブ](https://dashboard.stripe.com/webhooks)を開き、イベント宛先を作成します：

<Steps>

<Step>

**Create an event destination** をクリックし、**Your account** を選び、APIバージョンはアカウントの既定のままにします。

</Step>

<Step>

イベントタイプはちょうど1つだけ選びます——このチャンネルなら `charge.dispute.created`。[統合が必要とするイベントだけを購読する](https://docs.stripe.com/webhooks)のはStripe自身の推奨でもあり、ここではテンプレートを正直に保つ効果もあります。届くペイロードがすべて同じ形になるからです。

</Step>

<Step>

宛先タイプに **Webhook endpoint** を選び、EchobellのチャンネルURLを貼り付けます。

</Step>

<Step>

保存してから **Send test event**——あるいはCLIの `stripe trigger charge.dispute.created`——で、実際に電話が鳴ることを確認します。

</Step>

</Steps>

作成したチャンネルごとに繰り返します。Stripeは1アカウントあたり16個までwebhookエンドポイントを許可しているので、アラート段階ごとに1つずつでも十分足ります。

## ステップ3 — 実際に届くもの

Stripeは[Eventオブジェクト](https://docs.stripe.com/api/events/object)をJSONでPOSTします。Echobellはボディをそのまま読むので、すべてのフィールドがテンプレートと条件からドット記法で参照できます：

```json
{
  "id": "evt_1P...",
  "type": "charge.dispute.created",
  "livemode": true,
  "created": 1757548800,
  "data": {
    "object": {
      "id": "dp_1P...",
      "amount": 4900,
      "currency": "usd",
      "reason": "fraudulent",
      "status": "needs_response",
      "charge": "ch_3P...",
      "evidence_details": { "due_by": 1759449600 }
    }
  }
}
```

このペイロードで意外に思われる点が3つあります。

**金額は最小通貨単位の整数です。** `amount` が `4900` なら $49.00 です。Echobellのテンプレートは値の埋め込みと比較はしますが算術はしないので、`{{data.object.amount}}` は `4900` と表示されます。単位を正直にラベル付けする（`Amount: 4900 (cents)`）か、最後の節のフォワーダーで送信前に100で割ってください。

**タイムスタンプはUnix秒です。** `{{data.object.evidence_details.due_by}}` は日付ではなく `1759449600` になります。正確な時刻より締め切りの存在自体が重要なら、テンプレートから外し——チャージバックのページに表示されています——リンクテンプレートに仕事をさせましょう。

**フィールド名はオブジェクトごとに違います。** チャージバックは `amount`、請求書は `amount_due`・`customer_email`・`attempt_count`・`hosted_invoice_url`、入金は `failure_message` と `arrival_date`、早期不正警告は `fraud_type`・`actionable`、そして `charge` が単なる文字列IDです。存在しない変数はエラーではなく空文字列として描画されるため、別のチャンネルからコピーしたテンプレートは静かに壊れます。これが「イベントタイプごとに1チャンネル」の実務的な理由です。

## ステップ4 — 意志ではなく条件で絞る

チャンネル[条件](/ja/docs/conditions)はテンプレートと同じ式構文を波括弧なしで使い、配信の前に評価されます。

すべてのStripeチャンネルに設定すべきもの：

```
livemode == true
```

テストモードのトラフィック——自分で走らせた `stripe trigger`、同僚がサンドボックスでいじっているもの——はもう手元に届きません。これは配線が動くことを確認した後に追加してください。前ではなく。

請求書失敗のチャンネルでは、しきい値が小口アカウントをあなたの夜から締め出します：

```
livemode == true && data.object.amount_due > 20000
```

セント単位で「$200超」と読みます。初回の失敗すべてではなく、本当に止まっているリトライだけを見たいなら：

```
livemode == true && data.object.attempt_count > 1
```

もし1つのエンドポイントで複数のイベントタイプを1チャンネルに向けてしまった場合も、条件で分け直せます：

```
type == "charge.dispute.created" || type == "payout.failed"
```

## ステップ5 — 通知経路を「壊れるもの」の外に置く

ここがテンプレートより価値のある部分です。

本番のwebhookエンドポイントはフルフィルメントが起きる場所です。アクセスを付与し、DBに書き込み、領収書メールを送ります。つまりそれは、アプリが落ちたときに一緒に落ちるエンドポイントでもあります。落ちると、Stripeは[指数バックオフで最大3日間リトライ](https://docs.stripe.com/webhooks)し、メールを送ってきます——そして未配信webhookのメールは他のStripeメールと見た目がまったく同じなので、見つかるのは月曜になります。

webhookが静かに死ぬ原因は地味です。Stripeは `3xx` リダイレクトを失敗として扱うので、`http` を `https` に飛ばし始めたり末尾スラッシュを付け始めたりするエンドポイントはイベントを受け取らなくなります。TLS 1.2以上が必要なので、証明書の期限切れや設定ミスだけで十分です。先週誰かが追加したWAFルールの `403` でも同じです。

Echobellに直接向けた2つ目のエンドポイントは、そのどれとも運命を共にしません。別ホストの別URLで、証明書も別物で、あなたのアプリが倒れている間も鳴り続けます。この規則は一般化できます。*何かが壊れたと知らせる経路は、その壊れたものを経由すべきではない。*

とはいえ、自分のエンドポイントの失敗も見えてほしいはずです。おかしいと感じたらWorkbenchの **Event deliveries** タブを確認してください——イベントごとに `Delivered`・`Pending`・`Failed` と、各試行のHTTPステータスが表示されます。Stripeはダッシュボードから15日以内、CLIの `stripe events resend` なら30日以内のイベントを再送できるので、2週間以内に気づいた穴は埋められます。

<Callout type="warn">EchobellのチャンネルURLはベアラー資格情報です。持っている人は誰でもそのチャンネルを発火できます。Stripeを直接そこへ向けるということは、`Stripe-Signature` ヘッダーを誰も検証しないということで、URLの流出はデータ漏洩ではなく「偽アラート製造機」を生みます。リポジトリやスクリーンショットに残さず、漏れたら **Reset Token** を使ってください。このトレードオフが気になるなら次の節へ。</Callout>

## 任意 — 先に署名を検証する

Stripeの署名を実際に検証し、金額を通貨らしく整形したいなら、手前に小さなフォワーダーを置きます。次のCloudflare Workerはイベントを検証し、Stripeの要求どおり即座に `200` を返し、Echobellにはフラットなペイロードを送ります：

```js
import Stripe from "stripe";

export default {
  async fetch(request, env, ctx) {
    const stripe = new Stripe(env.STRIPE_SECRET_KEY);
    const body = await request.text();

    let event;
    try {
      event = await stripe.webhooks.constructEventAsync(
        body,
        request.headers.get("stripe-signature"),
        env.STRIPE_WEBHOOK_SECRET,
      );
    } catch {
      return new Response("invalid signature", { status: 400 });
    }

    const invoice = event.data.object;
    ctx.waitUntil(
      fetch(env.ECHOBELL_HOOK_URL, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          customer: invoice.customer_email || invoice.customer,
          amount: (invoice.amount_due / 100).toFixed(2),
          currency: invoice.currency.toUpperCase(),
          attempt: invoice.attempt_count,
          externalLink: invoice.hosted_invoice_url,
        }),
      }),
    );

    return new Response("ok", { status: 200 });
  },
};
```

整形をコード側で済ませたので、向こう側のテンプレートはずっときれいになります：

```
Title: 💳 Payment failed — {{currency}} {{amount}}
Body: Customer: {{customer}}
Attempt #{{attempt}}
```

`externalLink` は特殊変数です。リンクテンプレートが未設定なら、Echobellはこれを通知レコードのリンクとして使うので、ホスト型請求書ページがワンタップで開きます。

このトレードオフの形に注意してください。これはそれ自体が失敗しうるインフラであり、まさにステップ5が警告していたものです。妥当な妥協は、偽アラートが煩わしくなる大量配信のチャンネルでは署名を検証し、チャージバックのチャンネルは直結のままにすること。そこでは誤った着信のコストは一瞬の困惑で、逃したときのコストはチャージバックされた金額です。

## この構成で得られないもの

- **オンコールのローテーションとエスカレーションはありません。** 電話チャンネルを購読している全員が同時に鳴ります。4人なら利点、40人なら問題です。40人ならインシデント管理プラットフォームが要ります。
- **重複排除はありません。** Stripeは[イベント順序を保証せず](https://docs.stripe.com/webhooks)、同じイベントを複数回配信することがあります。1件のチャージバックで2回鳴ることはあり得ます。
- **確認応答はありません。** 誰かが見たという記録も、誰も反応しなかったときに次の人へ上げる仕組みもありません。
- **「決済が止まった」アラートはありません。** Stripeは何かが起きたときにイベントを出し、止まったときには出しません。チェックアウトが壊れれば、イベントは一つも発火しません。これには自分側で定期ジョブを走らせ、直近1時間の決済件数がゼロならチャンネルを叩く——[cronベースのデッドマンスイッチ](/ja/blog/cron-job-failure-alerts)が必要です。

## トラブルシューティング

**Stripeでは `200` なのに通知が来ない。** Echobellは配信しない場合でもJSONボディ付きで `200` を返します——Event deliveriesタブでレスポンスボディを見てください。長さが正しいトークンで `success: false` なら、チャンネルトークンが違います。`success` が `true` なら、原因はたいてい条件です。`livemode == true` は設計どおりテストイベントをすべて止めます。

**Stripeが `405 Method Not Allowed` を報告する。** チャンネルのPOST Onlyがオンで、何かがGETを送りました。Stripe自身は常にPOSTなので、これはリンクプレビューかブラウザのタブであってStripeではありません。

**通知は来るがフィールドが空。** テンプレートが違うオブジェクトを参照しています——請求書チャンネルで `{{data.object.amount}}` と書いているが、そこでのフィールドは `amount_due` です。実イベントを1件送り、ダッシュボードで開いてJSONを読んでください。

**数週間動いた後に配信が失敗し始めた。** 証明書と、URLの手前にあるリダイレクトを確認してください。チャンネルURL直結ではまれですが、自分でデプロイしたフォワーダーなら第一の容疑者です。

## よくある質問

### チャージバックが起きたときStripeは電話をかけてくれますか？

単体ではできません。Stripeはメール、ダッシュボード、`charge.dispute.created` イベント、そしてStripe Dashboardアプリを使っていればプッシュで通知します。実際に鳴らすには、そのイベントを購読タイプが**電話**のチャンネルへルーティングします。

### StripeとEchobellをつなぐのにコードは必要ですか？

いいえ。Stripeは公開されたHTTPS URLならどこへでもJSONをPOSTし、EchobellのチャンネルURLはまさにそれです。コードが要るのは、`Stripe-Signature` ヘッダーを検証したいときと、金額を整形したいときだけです。

### サードパーティのwebhook URLをStripeに渡して安全ですか？

意識的なトレードオフです。Stripeが送るペイロードには顧客と決済のメタデータが含まれ、Echobellは生のwebhookペイロードを永続保存しません——描画された通知はあなたの端末に残ります。手放すのは署名検証です。URLを知った人は説得力のある偽アラートを送れます。APIキーと同じ扱いにし、検証したいものにはフォワーダー方式を使ってください。

### アラートに $49.00 ではなく 4900 と出るのはなぜ？

Stripeは最小通貨単位の整数で金額を送り、Echobellのテンプレートは算術をしません。テンプレートに単位を書くか、フォワーダーで100で割ってから送ってください。

### テストモードのイベントで起こされないようにするには？

チャンネルに条件 `livemode == true` を追加します。Stripeはサンドボックスと `stripe trigger` のイベントをすべて `livemode: false` としてマークします。

### 共同創業者はシート課金なしで同じアラートを受け取れますか？

はい。チャンネルのリンクを共有すれば、購読者それぞれが自分の通知タイプを選べます。一人はチャージバックを電話で、もう一人は通常プッシュで受け取れ、購読者ごとの課金はありません。

### 決済成功にもアラートを付けるべき？

短期間だけ、しかも1件1件がまだ「出来事」でいられるくらい事業が小さいうちだけです。成功通知が日常になった瞬間から、それは本当に重要な通知への反応を削り始めます——[アラート疲れ](/ja/blog/fix-alert-fatigue-developer-guide)の中核にある仕組みです。

## まとめ

構成の全体は、Echobellチャンネル1つにつきStripeのイベント宛先を1つ、`livemode == true` の条件を1行、そして電話という段階をタイマー付きのイベントのために取っておく規律です。チャージバックと早期不正警告にはタイマーがあります。$9プランの更新失敗にはありません。それがあるふりをすることが、本当にタイマーの付いた一件を寝過ごす原因になります。

[iPhoneにEchobellをダウンロード](https://apps.apple.com/app/apple-store/id6743597198?pt=128151925&ct=blog-stripe-payment-failure-alerts-ja&mt=8)、または[Google Playで入手](https://play.google.com/store/apps/details?id=one.echobell.echobellandroid)して、まずチャージバックのチャンネルを作り、本番の何かをこの経路に託す前に `stripe trigger charge.dispute.created` を1回流してください。

---

## 関連

- [Webhook統合ドキュメント](/ja/docs/webhook)
- [チャンネル条件リファレンス](/ja/docs/conditions)
- [開発者のためのアラート疲れ解消ガイド](/ja/blog/fix-alert-fatigue-developer-guide)
- [cronジョブ失敗アラート](/ja/blog/cron-job-failure-alerts)
- [Zapierのwebhook通知をスマホへ](/ja/blog/zapier-webhook-notifications-to-phone)
