> ## Documentation Index
> Fetch the complete documentation index at: https://waffo.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook 配信契約 FAQ

> Waffo が Webhook を配信する際に適用する正確なルール：エンドポイントが応答するまでに許される時間、成功として扱われるレスポンスボディ、各イベントのコールバック URL がどこから来るのか、同じイベントが 2 回届いたり順序が入れ替わったりする理由、そして未入金の通知がどのようなものかを説明します。

公開ページには Webhook の形式と再試行スケジュールが記載されています。
[Webhook 概要](/docs/ja/developer-docs/webhook/overview)、
[Webhook の再試行](/docs/ja/developer-docs/webhook/retry)、
[Webhook イベントタイプ一覧](/docs/ja/developer-docs/webhook/event-types)、
[ハンドリングのベストプラクティス](/docs/ja/developer-docs/webhook/best-practices) です。

**このページは、その境界における契約です** — 配信がハッピーパスどおりに振る舞わないときに
出てくる疑問を扱います。事後に 1 件の配信を確認する方法は
[Webhook イベントログと手動再送](/docs/ja/support/portal-webhook-log-faq)、
イベントの辞書と一括再送については
[Webhook イベント辞書と一括再送](/docs/ja/support/webhook-events-and-resend-faq)
を参照してください。

## エンドポイントは何秒以内に応答する必要がありますか？

**15 秒**です。これは配信呼び出しの読み取りタイムアウトで、3 秒以内に確立できない接続は
それより早く失敗します。どちらを超えても配信失敗として数えられ、そのレコードは再試行
スケジュールに戻されます。

したがって、リクエストの中で業務処理を行わないでください。署名を検証し、イベントを永続化し、
応答し、処理は非同期に行う — これが
[ベストプラクティスのページ](/docs/ja/developer-docs/webhook/best-practices)
が推奨するパターンです。応答する前に自社のサービスを 3 つ呼び出すハンドラーが、
正しく書かれた連携が `FAILED` として現れる最も一般的な理由です。

## HTTP 200 を返しています。なぜ配信が失敗として数えられるのですか？

判定を決めているのがステータスコードではないためです。判定は**レスポンスボディ**を読み、
配信が成功と数えられるのは次の両方が成り立つ場合だけです。

1. レスポンスが **2xx** であること — 4xx や 5xx はボディが読まれる前に失敗します。
2. ボディが、値が `success` である `message` フィールドを持つ **JSON オブジェクト**であること。

それ以外はすべて失敗します。`failed` や `unknown`（どちらも「再送してほしい」という意味です）
のボディ、空のボディ、異なるフィールド名、HTML のエラーページ、そもそも JSON として
パースできないボディなどです。

驚きの大半を説明する具体的な点が 3 つあります。

| 質問 | 回答 |
| - | - |
| 文字列だけのボディ `success` は受け付けられますか？ | **いいえ。** JSON オブジェクトである必要があります — `{"message":"success"}`。プレーンテキストの `success` は拒否されます。 |
| 値は大文字と小文字を区別しますか？ | いいえ。`success`、`Success`、`SUCCESS` はいずれも受け付けられます。 |
| フィールド名は大文字と小文字を区別しますか？ | **はい。** `message` は小文字である必要があります。`Message` は読み取られません。 |
| 余分なフィールドがあると壊れますか？ | いいえ。追加のフィールドは無視されます。 |

## レスポンスの Content-Type は application/json でなければなりませんか？

実務上はそうです — `Content-Type: application/json` を送ってください。レスポンスは JSON として
読まれるため、`text/plain` や `text/html` として配信されたボディはパースできず、
バイト列自体が `{"message":"success"}` であっても配信は失敗として記録されます。
公開されている [概要](/docs/ja/developer-docs/webhook/overview) が加盟店のレスポンスに
このヘッダーを示しているのは、まさにこの理由によります。

## 署名検証に失敗した場合、リクエストを拒否すべきですか、それともとにかく処理すべきですか？

決して処理しないでください。まず検証し、検証が通ってからのみイベントに基づいて行動して
ください。これは
[受け入れ要件](/docs/ja/developer-docs/tools-and-references/references/acceptance-criteria)
でもあります。検証に失敗した場合、ローカルの状態を更新してはならず、代わりに照会 API から
本当のステータスを読み取る必要があります。

何を*返す*かはそこから導かれます。`{"message":"failed"}` と応答してください。これにより
レコードは再試行スケジュールに残ります。そして原因を突き止めてください。検証が通らない署名は
どちらかの側の設定の問題であり、再試行によって自然に直ることはありません。再試行は修正では
なく猶予として扱い、その間は照会 API で突き合わせてください。

ペイロードを再シリアライズした後にのみ署名検証が失敗する場合、それが原因です。
**生のリクエストボディのバイト列**に対して検証してください。パースして文字列化し直した
コピーに対しては決して行わないでください。フィールドの順序、空白、数値の書式はすべて
往復によって変わりますが、署名は元のバイト列に対して計算されています。
[Webhook 署名検証](/docs/ja/developer-docs/webhook/signature-verification) を参照してください。

## 「8 回の再試行」とは正確に何を意味し、間隔は保証されますか？

**初回を含めて** 8 回の試行で、間隔は広がっていきます。スケジュールは
[Webhook の再試行](/docs/ja/developer-docs/webhook/retry) にあり、全体でおよそ 20 時間に
わたります。

スケジュールだけでは分からない留保が 2 つあります。

* **間隔は最小値であって、約束の時刻ではありません。** 再試行は予定時刻に*適格*になり、
  配信ワーカーが次にそれを拾ったときに送出されます。滞留があれば、試行は表が示すより遅く
  着地することがあります。再試行が正確な時刻に届くと仮定するロジックは決して作らないでください。
* **予算を使い切ると、そのレコードは最終的に失敗となり**、それ以上の自動試行は行われません。
  Portal では、次回再試行時刻のない `FAILED` レコードとして表示されます。

## 「次回再試行時刻」が空になることがあるのはなぜですか？

自動再試行が実際にまだ予定されている間だけ値が入るためです。このフィールドは、失敗し*かつ*
再試行の予算が残っているレコードでは埋まり、配信済みのレコードや再試行を使い切ったレコード
では空になります。

そのため、実際に読むべきはステータス単独ではなく、2 つの列の組み合わせです。

| ステータス | 次回再試行 | 読み方 |
| - | - | - |
| Failed | タイムスタンプ | まだ予定されている。そのままにしておく |
| Failed | 空 | 予算切れ。手動再送でしか動かない |
| Successful | 空 | 配信され、受理された |

## すべての試行が失敗した後、どうすればよいですか？

対象オブジェクトの現在の状態をその照会 API から読み取り、突き合わせてください。信頼できる
状態は常に通知ではなくオブジェクト側にあります。
[障害からの復旧のガイダンス](/docs/ja/developer-docs/webhook/retry) も同じ 3 つの手順を
挙げています。オブジェクトを照会する、定期的に一括で突き合わせる、そして配信がまったく
届かない場合はサポートに連絡する、です。

エンドポイントを修正した後は、Portal から単一、複数選択、または日付範囲指定のモードで
再送することもできます。1 リクエストあたり最大 **50 件**です。なお、まだ壊れたままの
エンドポイントに対する再送は同じ予算から 1 回分の試行を消費します。まず修正し、それから
再送してください。詳細は
[Webhook イベント辞書と一括再送](/docs/ja/support/webhook-events-and-resend-faq) にあります。

## あるレコードで Resend がグレーアウトしているのはなぜですか？

レコードを再送できない理由はちょうど 3 つです。

| 理由 | 意味 |
| - | - |
| そのレコードにコールバック URL が保存されていない | 送り先がありません。再送が宛先を発明することはできません |
| そのレコードがまだ初回配信の最中である | 進行中の試行は再送できません。決着するまで待ってください |
| そのレコードが貴社のものではない | セッションの加盟店 ID に属していません |

このリストに**ない**ものに注目してください。すでに配信に成功したレコードは再送**できます**。
これは意図的です。Waffo が受理として記録した配信であっても、貴社側で処理されていない
可能性があるため、再送が許されています。

再送には、Portal のロールに webhook 再送の権限も必要です。権限がなければ、そのアクションと
行の選択は無効表示になるのではなく、そもそも描画されません。したがって再送のコントロールが
まったく見えない運用者は、レコードの適格性ではなく権限の問題を見ていることになります。
Portal の Admin に依頼してください。

## 各イベントのコールバック URL はどこから来ますか？

これは「通知が届かなかった」の最も一般的な原因です。URL が解決されず、何も送信されず、
レコードも書き込まれていないのです。

| イベント | URL の取得元 | Portal で設定可能か |
| - | - | - |
| `PAYMENT_NOTIFICATION` | 注文作成リクエストの `notifyUrl` — **必須**、最大 1024 文字 | いいえ |
| `REFUND_NOTIFICATION` | 返金リクエストの `refundNotifyUrl`。ない場合は Portal で設定した Refund Notify URL | はい、フォールバックとして |
| `CHARGEBACK_NOTIFICATION` | Portal で設定した Chargeback Notify URL のみ | はい、それのみ |
| `TOKENIZATION_NOTIFICATION` | トークン生成リクエストの `notifyUrl`。決済中にトークン化されたカード（今後の利用のためのバインド）については、Portal で設定した Tokenization Notify URL | はい、決済時のトークン化について |
| サブスクリプション系イベント | サブスクリプション作成リクエスト由来の、サブスクリプションが保持する `notifyUrl` | いいえ |
| `PAYOUT_NOTIFICATION` | 送金リクエストの `notifyUrl` — API で作成された送金では**必須** | いいえ |
| `WITHDRAW_NOTIFICATION` | 出金リクエストの `notifyUrl` — 加盟店起点の出金では必須 | いいえ |

設計に織り込む価値のある帰結が 2 つあります。

* **グローバルな決済通知 URL は存在しません。** 決済通知は注文作成時に渡した `notifyUrl` に
  のみ送られ、そのフィールドが必須なのはまさにフォールバックが存在しないためです。
  Portal で 1 つのアドレスを設定してパラメータの送信をやめる、ということはできません。
* **チャージバック通知にはリクエストレベルのパラメータがありません。** Portal の URL が
  空の場合、それらのイベントは単に配信されず、ログのレコードも残りません。Webhook ログ
  ページでチャージバックの結果が空である場合、ほぼ確実に Chargeback Notify URL が一度も
  設定されていません。決済中にトークン化されたカードのトークン化通知についても同じことが
  当てはまります。`notifyUrl` のない単独のトークン化リクエストも同様に通知されません。

Portal 側の 3 つの URL の設定については
[Portal セルフサービスのセキュリティ設定](/docs/ja/support/portal-security-config-faq)
で扱っています。

## 通知 URL を変更しました。すでに再試行キューにある通知はどこに届きますか？

**古い URL にです。** コールバック URL は各通知レコードの作成時にそのレコードに保存され、
すべての再試行も、すべての手動再送も、保存された値に送信されます。Portal の設定を編集する、
あるいは新しい注文で別の `notifyUrl` を渡すことは、その後に作成されるレコードにのみ
影響します。

URL の移行はそれに合わせて計画してください。新しい URL を保存した瞬間に切り替えるのではなく、
進行中の分がはけるまで古いエンドポイントでトラフィックを受け続けてください。

## 通知 URL のフィールドは何を受け付けますか？ HTTPS でなければなりませんか？

保存時のバリデーションが受け付けるのは次のとおりです。

* `http://` または `https://` で始まる URL
* ASCII 文字のみ、空白なし
* ホストを持つパース可能なアドレス — **ポート、パス、クエリ文字列はいずれも許可されます**
* Refund Notify URL と Chargeback Notify URL は最大 512 文字
* 空の値。設定をクリアします

**それでも HTTPS を使ってください。** `http://` はバリデーションを通りますが、公開されている
[設定要件](/docs/ja/developer-docs/webhook/overview) は TLS 1.2 以上の HTTPS を求めており、
Webhook は決済データを運びます。

バリデーションのルールからは分かりにくい厳格な制約が 1 つあります。**TLS 証明書はパブリックな
認証局によって信頼されている必要があります。** 配信クライアントは例外の経路を持たない
デフォルトのトラストストアを使用するため、自己署名やその他の信頼されない証明書は接続に失敗し、
すべての試行が失敗します。プライベート CA はここでは使用できません。

## 同じイベントが複数回届くことはありますか？ 何をキーに重複排除すべきですか？

はい、重複はありえます。ハンドラーは冪等である必要があります。受け入れ基準もまさにこれを
テストします。繰り返しの原因には、内部的な再配信、手動再送、そして同じ注文についての
オーソリ段階の通知とその後の最終通知が含まれます。

**ペイロードにイベント ID や配信 ID はありません。** ボディは
`{"eventType": ..., "result": {...}}` であり、注目すべきヘッダーは署名だけです。したがって、
**`eventType` と `result` の中の業務識別子** — そのイベントが対象とする決済注文、返金注文、
サブスクリプション、送金注文、出金注文、またはトークン — に、貴社が処理しようとしている
ステータスを組み合わせて重複排除してください。識別子だけで重複排除すると、オーソリをすでに
記録した注文の最終ステータスを取りこぼすことになります。

## 到着順序は保証されますか？「失敗」が「成功」より先に届くことはありますか？

いいえ、順序は保証されません。そして、はい — 順序が入れ替わった通知を受け取ることがあります。
再試行、キューイング、ネットワーク遅延はいずれも配信の順序を入れ替えます。

これを安全にするルールは
[ベストプラクティスのページ](/docs/ja/developer-docs/webhook/best-practices) にあります。
**状態機械を到着順序で駆動しないでください。** 各通知はトリガーとして扱い、終端の状態は
オブジェクトから取得してください。ペイロードのステータスフィールドから、後戻りを防ぐガードを
付けて取得するか、通知のたびに照会 API を呼び出してください。サブスクリプションではこれが
文書化されたパターンです。ステータス通知か期間変更通知のいずれかを受けたら、
サブスクリプション照会を 1 回呼び出し、その結果に対して単一の統一フローを実行します。

## オーソリ後にキャプチャする注文では、オーソリ済みの段階で通知が届きますか？

はい。それは別のイベントタイプとしてではなく、最終通知と**同じ注文に対する
`PAYMENT_NOTIFICATION`** として届きます。両者を区別するのはイベントではなく、
ペイロードの注文ステータスです。

| `orderStatus` | 意味 |
| - | - |
| `AUTHORIZATION_REQUIRED` | 顧客のオーソリ待ち |
| `PAY_IN_PROGRESS` | 決済処理中 |
| `AUTHED_WAITING_CAPTURE` | オーソリ済み。貴社のキャプチャ呼び出し待ち |
| `CAPTURE_IN_PROGRESS` | キャプチャ処理中 |
| `PAY_SUCCESS` | 終端 — 支払済み |
| `ORDER_CLOSE` | 終端 — 未払い |

これは、注文 ID だけで重複排除するのが誤りである理由でもあります。ここでは 1 つの注文に対して
2 件の通知が想定されており、終端なのはそのうち 1 件だけです。
[オーソリとキャプチャ](/docs/ja/essentials/auth-capture) を参照してください。

## 通知が終端でないステータスを運ぶことはありますか？ 再照会すべきですか？

はい — 上の表が示すとおり、注文は進行中のまま通知されることがあります。終端のステータスに
基づいて行動し、終端でないものは記録して待ってください。その時点で確実性が必要なら、
推測するのではなく照会 API を呼び出してください。

貴社が自ら注文をクローズしてはならない唯一のステータスは、結果不明です。`E0001` の場合は
注文を開いたままにし、同一のパラメータで再試行し、照会結果か通知を待ってください。
自らクローズすることは文書化された受け入れ不合格です。
[連携の受け入れ基準](/docs/ja/developer-docs/tools-and-references/references/acceptance-criteria)
を参照してください。

## 注文がキャンセルされた、タイムアウトした、失敗したとき、通知は届きますか？

はい — 3 つとも `PAYMENT_NOTIFICATION` を生成し、3 つとも同じ終端ステータスに着地します。
**`ORDER_CLOSE`** です。個別の `CANCELLED`、`EXPIRED`、`FAILED` というステータス値は
存在しません。

これらを区別するには、ペイロードの `orderFailedReason` が運ぶ失敗の詳細を読んでください。
そこには `orderFailedCode` と `orderFailedDescription` が含まれます。したがって正しい
ハンドリングの形はこうです。`ORDER_CLOSE` は「履行しない」を意味し、`orderFailedReason` が
顧客に何と伝えるべきか、再試行に意味があるかを教えてくれます。

## イベントを購読する必要がありますか、それともすべて自動的に発火しますか？

購読の登録簿はなく、イベントごとのオプトインもありません。イベントは、上の表に従って
コールバック URL が解決されるたびに配信されます。つまり、何を受け取るかの制御は、
どの URL が存在するかの制御そのものです。

最小限の連携における実務的な読み方はこうです。注文作成時に `notifyUrl` を渡して決済通知を
処理し、ハンドラーを作っていないイベント系統については Portal で設定する URL を空のままに
しておき、それらの状態は代わりに照会 API から拾ってください。列挙できるすべての
オブジェクトでポーリングが機能します。チャージバックは `/api/v1/chargeback/list` と
`/api/v1/chargeback/inquiry`、トークンの状態は `/api/v1/tokenization/inquiry` です。
代償はレイテンシです。そしてチャージバックについては、証拠の提出期限が案件の作成時点から
進行するため、異議申し立てを少しでも扱うなら、ポーリングに頼るのではなく
Chargeback Notify URL を設定してください。

## Portal の Webhook ページで、送金と出金の通知を見つけられますか？

このページからは見つけられません。Webhook ログのページが検索するのは固定された 7 つの
イベント系統 — 決済、返金、トークン化、チャージバック、そして 3 つのサブスクリプション
イベント — であり、送金と出金の通知はそこに含まれていないため、どのようにフィルターしても
表示されません。

それらについては、送金と出金の照会 API から状態を読み取ってください。また、設定上の
非対称性にも注意してください。どちらも Portal で設定する URL を持たないため、その通知先は
それらを作成したリクエストで渡した値そのものです。

## 通知が一度も配信されなかったことを、どうすれば分かりますか？

現時点では、知らされるのではなく自分で見にいくことによってです。配信が失敗したときの
加盟店向けのアラートはなく、通知レコードを照会する加盟店向け API もありません。
方法は 2 つです。

1. **Portal の Webhook ログ**、その 90 日のウィンドウ内で — 失敗で絞り込み、
   次回再試行の列を使って「まだ再試行中」と「最終的に失敗」を切り分けます。
2. **照会 API に対する突き合わせ。** 終端の状態を期待しているオブジェクトを定期的に照会し、
   自社のレコードと比較します。これが推奨される安全網であり、Portal ページが追跡しない
   イベントもカバーする唯一の方法です。

2 つ目を作ってください。1 つ目では見えないケース — URL が解決されなかったために
そもそも作成されなかったイベント — を捕まえられるのはこちらです。

## 許可リストに登録できる固定の IP アドレスから配信していますか？

Webhook 配信について公開されている送信元 IP レンジはなく、静的な送信元アドレスも設定されて
いません。したがって固定できるものはありません。Waffo のコールバック元を IP で許可リストに
登録することは、設計の前提にすべきものではありません。

代わりにすべきことは、送信元ではなくコールバックそのものを認証することです。すべての Webhook は
生のボディに対する署名を運んでおり、Waffo の秘密鍵で署名され、**Merchant Portal → Integration**
から取得できる Waffo の公開鍵で検証できます。検証済みの署名は送信元 IP の一致より強い保証で
あり、インフラが変わっても壊れません。

## 開発中に自分のノート PC でコールバックを受け取るにはどうすればよいですか？

`notifyUrl` はパブリックなインターネットから到達可能である必要があります。`localhost` や
プライベートアドレスには到達できません。トンネリングツールを使い、そのトンネルの公開 URL を
`notifyUrl` として登録してください。公開されている
[概要](/docs/ja/developer-docs/webhook/overview) に、よく使われる 2 つのツールのコマンドが
示されています。

実務上の点が 2 つあります。トンネルのホスト名は再起動のたびに変わるのが普通なので、
URL は Portal の設定に保存するのではなく注文ごとに渡してください。また、提示される証明書は
パブリックに信頼されたものである必要があります。ホスト型のトンネルはそれを提供しますが、
ローカルの自己署名サーバーは提供しません。

***

サンドボックスの配信も、再試行スケジュールを含めてこの同じ契約に従います。そもそも
サンドボックスで何を起こせて何を起こせないかについては
[サンドボックスのシミュレーション制限 FAQ](/docs/ja/support/sandbox-simulation-limits-faq)
を参照してください。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.