Localhost で Paddle Webhook シミュレーターを使用するには、HTTPS トンネルを介してローカル ハンドラーを公開し、パブリック URL を指すサンドボックス通知宛先を作成して、その宛先に対して単一イベントまたはライフサイクル シミュレーションを実行します。 Paddle は現実的なリクエストを次のように送信します。 Paddle-Signature ヘッダーを使用するため、同じ raw-body 検証パスをローカルと運用環境で実行できます。
Paddle のシミュレータが単なるサンプル ペイロード以上のものである理由
コピーされた JSON フィクスチャは switch ステートメントをテストします。 Paddle のシミュレーターは、ネットワーク宛先、プロバイダー ヘッダー、署名シークレット、応答ステータス、およびイベント スキーマを一緒にテストします。役人 Webhook シミュレータの概要 再利用可能な単一のイベントとシナリオをサポートします。単一イベントは 1 つのイベント タイプを対象とし、実行後にカスタマイズできます。シナリオは、サブスクリプションの作成や更新などのライフサイクルに対して事前定義されたシーケンスを送信します。
シナリオ構成では、ペイロードに既存の Paddle エンティティを設定し、フローを変更できます。各実行では、ペイロード、アウトバウンド要求、エンドポイント応答を検査できるシミュレーション実行イベント レコードが生成されます。そのため、シミュレーターはルートがオンラインであることを証明するだけでなく、状態遷移のデバッグにも役立ちます。
ローカル エンドポイントを Paddle サンドボックスに接続する
- アプリケーションを起動します。たとえば、
http://localhost:3000. - 次のような POST ルートを追加します。
/api/webhooks/paddle. - 走る
npx portpreview 3000HTTPS アドレスをコピーします。 - Paddle サンドボックスで、次を使用して通知先を作成します。
https://your-subdomain.portpreview.dev/api/webhooks/paddle. - その宛先のエンドポイント シークレットを公開してコピーします。
PADDLE_WEBHOOK_SECRET。それはあなたの API キーではありません。 - [開発者ツール] → [シミュレーション] を開き、単一のイベントまたはシナリオを選択し、宛先を選択して構成し、実行します。
- Paddle のシミュレーション実行応答をローカル ログおよび永続化された Webhook 受信と比較します。
全体を通してサンドボックスのリソースと認証情報を使用します。ライブ宛先シークレットとサンドボックス シミュレーター リクエストを混合すると、両方の文字列が妥当であるように見えても、署名が失敗することが保証されます。
を確認してください Paddle-Signature ヘッダ
Paddle は、通知先に属するシークレットを使用して各 Webhook に署名します。ヘッダーには、次のように識別される Unix タイムスタンプを含むコンポーネントが含まれます。 ts およびによって識別される 1 つ以上の署名 h1。 Paddle は署名バージョンを追加する可能性があるため、ヘッダーに 1 つの裸のハッシュが含まれていると想定するのではなく、ヘッダーを解析します。
署名されたペイロードは、タイムスタンプ、コロン、および未加工のリクエスト本文です。 Paddle は、エンドポイント シークレットを使用して HMAC-SHA256 を適用します。その 署名確認書類 利用可能な場合は公式 SDK を推奨し、手動アルゴリズムを文書化します。リプレイ攻撃を制限するには、SDK のタイムスタンプ許容値または検証ツールでの明示的な許容値を常に強制してください。
Node の公式 SDK を優先する
import express from 'express';
import { Environment, Paddle } from '@paddle/paddle-node-sdk';
const app = express();
const paddle = new Paddle(process.env.PADDLE_API_KEY, {
environment: Environment.sandbox,
});
app.post(
'/api/webhooks/paddle',
express.raw({ type: 'application/json' }),
async (req, res) => {
const signature = req.header('paddle-signature') ?? '';
const rawBody = req.body.toString('utf8');
try {
const event = await paddle.webhooks.unmarshal(
rawBody,
process.env.PADDLE_WEBHOOK_SECRET,
signature,
);
await acceptOnce(event.eventId, event);
return res.status(200).send('accepted');
} catch (error) {
return res.status(400).send('invalid webhook');
}
},
);
app.listen(3000);
マウント express.raw() グローバルの前に express.json() このルートのミドルウェア。 Next.js App Router などの Fetch スタイルのフレームワークでは、次を使用します。 await request.text() 一度実行して、その正確な文字列を SDK に渡します。役人 Webhook クイックスタート この生の身体の要件を示しています。
手動検証で行う必要があること
- JSON 正規化を行わずに生の本文を読み取ります。
- セミコロンで区切られたすべてのヘッダー コンポーネントを解析し、サポートされているものを収集します
h1価値観。 - 欠落しているもの、形式が間違っているもの、または不当に古いものを拒否する
ts. - コンピューティング
HMAC_SHA256(secret, ts + ':' + rawBody). - タイミングセーフな比較を使用して、期待されるダイジェストと候補署名を比較します。
- 一致した後のみ、JSON を解析してディスパッチします
event_type.
複数の署名が存在する可能性がある場合、シークレットのローテーション中にサポートされている有効な署名を受け入れることが重要になります。ヘッダーを分割して、やみくもに 2 番目の項目を取得しないでください。不一致の診断中に、エンドポイント シークレットや完全な顧客ペイロードをログに記録しないでください。
請求の不変条件に基づいたシミュレーションを設計する
サブスクリプションの作成
サブスクリプション作成シナリオを実行し、ネットワーク到着順序を 1 つと仮定せずに、データベースが関連するトランザクションおよびサブスクリプション イベントを受信できることを確認します。 Paddle エンティティ ID を保存し、レコードを冪等に更新します。リクエストが再送信された場合でも、アプリケーションは 1 つの権利だけを付与する必要があります。
更新と支払いの失敗
シナリオ構成で利用可能な更新パス、期限切れパス、および回復パスを正常に実行します。製品アクセス ポリシーから請求ステータスを分離する: Paddle が収集の問題を報告している間、猶予期間により意図的にアクセスがアクティブのままになる場合があります。
キャンセルと予定変更
キャンセル予定のサブスクリプションと、実質的にキャンセルされたサブスクリプションを区別します。保存する next_billed_at、スケジュールされた変更データ、およびプロバイダーのステータスを、ライフサイクルを折りたたむのではなく、 is_active.
エンティティの更新
キャッシュが消費する製品、価格、顧客、サブスクリプションの更新をシミュレートします。ハンドラーは未知のイベント タイプを安全に無視し、署名が有効な場合はそれらを承認する必要があります。将来パドルを追加しても、無限の失敗ループにならないようにしてください。
実行ごとの冪等性と順序付け
Webhook イベントの安定 ID を受信テーブルの一意のキーとして使用します。 1 つのトランザクションで、レシートを挿入し、状態変更を適用し、副作用をキューに入れます。挿入が競合する場合は、作業を繰り返さずに成功を返します。多くの正規の更新は 1 つのサブスクリプションを対象とする可能性があるため、エンティティ ID のみで重複排除を行わないでください。
イベントは順序どおりに到着しない可能性があります。破壊的な遷移を適用する前に、イベントの発生時間を比較するか、最新のパドル エンティティを取得します。プロバイダーの状態とタイムスタンプを保存し、新しい更新を上書きする古い更新を拒否します。シミュレーション シナリオは、独立したフィクスチャではなく接続されたシーケンスを生成するため、これらの仮定をテストするのに最適です。
シミュレーターからローカルホストへの障害のトラブルシューティング
宛先が 404 または 405 を返す
パブリック URL に完全なルートが含まれていること、およびルートが POST をエクスポートしていることを確認してください。ブラウザーの GET は、POST 専用 Webhook の有効なテストではありません。使用 curl -X POST パブリック URL に対して、トンネル ルーティングとパドル設定を区別します。
宛先は 400 を返します
かどうかを検査します。 Paddle-Signature が到着し、エンドポイント シークレットが選択した通知先に属していることを確認します。ボディ パーサー、リクエスト ロガー、ミドルウェアがボディを消費または再フォーマットしていないことを確認します。に記載されているように、シミュレーターは検証可能な署名を送信します。 パドルのシミュレーション実行ガイド.
宛先が 500 を返すか、タイムアウトになります
すぐに持続して、電子メール、プロビジョニング、およびアウトバウンド API 呼び出しをキューに移動します。永続的な受け入れ後に 2xx を返します。不必要な失敗の一般的な原因は、見慣れないイベント タイプのスローです。それを記録して確認するデフォルトのブランチを使用します。
シナリオでは予期しないデータが使用されています
シミュレーションで自動的に生成された値が使用されているか、実際のサンドボックス エンティティが設定されているかを確認します。以前の実行を反映していると仮定するのではなく、構成されたシナリオ オプションと実行イベント ペイロードを検査します。
生産前のセキュリティチェックリスト
- API キーと通知先のシークレットを分離し、サーバーのみに保管します。
- 処理する前に、生の本文、ヘッダー署名、およびタイムスタンプを検証します。
- タイミングセーフな比較または公式の SDK 検証ツールを使用します。
- イベント ID の重複を排除し、順序どおりでない更新を処理します。
- 本文制限、POST 専用ルーティング、編集されたログ、およびレート制御を適用します。
- 個別のサンドボックスとライブの宛先を使用し、運用 URL を切り替える前にシミュレーションと実際のサンドボックス フローを繰り返します。
シミュレーターは、制御されたライフサイクル シーケンスの下での統合を証明します。実際のサンドボックス チェックアウトでは、チェックアウトとエンティティの関係も証明されます。両方を使用してから、一般的な内容を確認してください Webhook 署名ガイド そして べき等性ガイド 打ち上げ前。
