Polar Webhook をローカルでテストするには、Webhook ハンドラーを実行し、Polar CLI をインストールして認証し、次を使用します。 polar listen http://localhost:3000/api/webhooks/polar 署名されたイベントをマシンに直接中継します。 リスナーによって出力された一時シークレットをコピーします。 POLAR_WEBHOOK_SECRET、処理する前に生の本文を検証し、サンドボックス チェックアウト、注文、返金、またはサブスクリプション イベントをトリガーします。
Polar CLI が最もシンプルなローカル ワークフローである理由
通常の運用 Webhook エンドポイントは、HTTPS 経由でパブリックにアクセスできる必要があります。 Polar は専用のローカル リスナーを提供するため、永続的なエンドポイントを作成したり、一時的なコールバック URL をダッシュボードに繰り返し貼り付けたりする必要はありません。 CLI は組織に接続し、セッション署名シークレットを表示し、イベントを受信して、指定したローカル URL に転送します。
ポラールの公式 ローカル Webhook ドキュメント 用途 polar listen http://localhost:3000/。アプリケーションが次のようなルートの下で Webhook を処理する場合は、完全なパスを指定します。 /api/webhooks/polar。これにより、実稼働前にパスの間違いが検出され、デプロイする予定の同じアプリケーション ハンドラーが維持されます。
CLIをインストールしてリスニングを開始する
- アプリケーションを起動し、その POST ルートがローカルで利用できることを確認します。
- 公式ドキュメントのコマンドを使用して、Polar CLI をインストールします。
- 走る
polar loginそして正しいアカウントを認証します。 - スタート
polar listen http://localhost:3000/api/webhooks/polar. - サンドボックス製品を所有する組織を選択します。
- 表示されたセッション シークレットをコピーします。
POLAR_WEBHOOK_SECRET起動時にのみ環境が読み込まれる場合は、アプリを再起動します。 - サンドボックス アクションをトリガーし、CLI 配信ステータスをローカル ログと比較します。
リスナーのシークレットはローカル セッションの一部です。これがダッシュボード エンドポイント シークレットと等しいと想定したり、運用環境の値を再利用したりしないでください。再接続して別のシークレットを受信した場合は、再度テストする前にローカル環境を更新してください。
Next.js App Router ハンドラーを作成する
Polar は標準 Webhook 仕様に従います。配送内容には以下が含まれます webhook-id, webhook-timestamp、そして webhook-signature。検証では、正確な生のリクエスト本文を使用する必要があります。電話をかける request.json() 最初にオブジェクトを再度シリアル化すると、署名付きバイトが変更される可能性があります。
// app/api/webhooks/polar/route.ts
import { Webhook } from 'standardwebhooks';
export const runtime = 'nodejs';
export async function POST(request: Request) {
const rawBody = await request.text();
const secret = process.env.POLAR_WEBHOOK_SECRET;
if (!secret) {
return new Response('Webhook secret is not configured', { status: 500 });
}
const headers = {
'webhook-id': request.headers.get('webhook-id') ?? '',
'webhook-timestamp': request.headers.get('webhook-timestamp') ?? '',
'webhook-signature': request.headers.get('webhook-signature') ?? '',
};
try {
const encodedSecret = Buffer.from(secret.trim(), 'utf8').toString('base64');
const payload = new Webhook(encodedSecret).verify(rawBody, headers);
await acceptPolarEvent(headers['webhook-id'], payload);
return Response.json({ received: true });
} catch {
return new Response('Invalid signature', { status: 403 });
}
}
ポーラーズ 納品書類 一般的なカスタム検証トラップを呼び出します。標準 Webhook ライブラリは Base64 でエンコードされたシークレットを期待しますが、Polar は通常のシークレット文字列を提供します。 Polar SDK ヘルパーは、この変換を自動的に処理します。使用するとき standardwebhooks 直接、上記のように完全な Polar シークレットを Base64 エンコードします。
利用可能な場合は公式のフレームワーク アダプターを優先します
Polar は、署名検証と型指定されたペイロード ハンドラーを組み合わせたアダプターを公開しています。役人 Express アダプターのドキュメント チェックアウト、注文、返金、特典、およびサブスクリプション イベントに対する詳細なコールバックを提供します。アダプターを使用すると、ヘッダー解析やシークレット エンコーディングが誤って実装される可能性が減ります。
import express from 'express';
import { Webhooks } from '@polar-sh/express';
const app = express();
app.use(express.json());
app.post('/webhooks/polar', Webhooks({
webhookSecret: process.env.POLAR_WEBHOOK_SECRET,
onOrderPaid: async (event) => {
await queueOrderPaid(event.data);
},
onSubscriptionActive: async (event) => {
await queueSubscriptionActive(event.data);
},
}));
app.listen(3000);
インストールするバージョンのアダプターに記載されているミドルウェアの順序に従ってください。代わりに汎用ベリファイアを使用する場合は、生の本文を明示的に保存してください。ストリームを読み取るアダプターと、ストリームを最初に消費するミドルウェアを混合しないでください。
どの極地イベントをテストする必要がありますか?
チェックアウトイベント
チェックアウト イベントは、有料注文になる前にセッションを追跡するのに役立ちます。チェックアウトが作成されたという理由だけでアクセスを許可することなく、成功した完了と放棄または更新された状態をテストします。有料の注文またはアクティブなサブスクリプションは、引き続き権限のある権利シグナルである必要があります。
注文の支払いと返金が完了しました
注文支払いイベントの場合は、Polar の顧客と製品を内部アカウントにマッピングし、購入した特典を 1 回だけ付与します。返金イベントでは、返金ポリシーに従って、影響を受ける権利のみを取り消す必要があります。サポートがPolarとイベントを調整できるようにプロバイダーIDを保存します。
サブスクリプションのライフサイクル イベント
テストの作成、アクティブ化、更新、キャンセル、取り消し、および製品がサポートする期限を過ぎた動作。キャンセルリクエストを行っても、アクセスがすぐに終了するとは限りません。ライフサイクルを 1 つに短縮するのではなく、ステータスと発効日を保存します is_active 旗。
給付金助成金
統合で Polar の特典を使用する場合は、請求イベントとは別に付与の作成、更新、取り消しをテストします。ハンドラーが望ましい状態に収束するようにすることで、同じ許可を 2 回受信しても重複したリソースがプロビジョニングされなくなります。
すべてのイベントをべき等にする
ネットワーク配信は、1 回限りのトランザクションではありません。ハンドラーはデータベース作業をコミットして応答を失い、送信側が再試行する可能性があります。使用する webhook-id 一意の配信キーとして取得し、アプリケーションの状態を更新する同じトランザクションでそれを要求します。
await db.transaction(async (tx) => {
const firstDelivery = await tx.webhookReceipts.insertIfAbsent({
provider: 'polar',
deliveryId: webhookId,
});
if (!firstDelivery) return;
await applyPolarEvent(tx, payload);
await tx.outbox.enqueue('polar-event-accepted', { webhookId });
});
すでに完了した重複に対して 2xx 応答を返します。多くの正当なイベントが同じリソースを対象としているため、顧客 ID またはサブスクリプション ID のみによって重複排除を行わないでください。の Webhook の再試行とべき等性ガイド 受信トレイと送信トレイのパターンについて詳しく説明します。
確認作業は短くする
リクエストを確認し、永続化するか永続ジョブをキューに入れて、成功を返します。電子メール、ライセンス生成、リポジトリ アクセス、分析、およびサードパーティ API 呼び出しは非同期で実行する必要があります。高速確認応答により再試行が削減され、永続書き込みにより、プロセスが戻った後に終了した場合にイベントが消えることが防止されます。
未知のイベント タイプは、有効な署名検証後に記録され、確認される必要があります。 Polar は、時間の経過とともにイベント タイプを追加できます。見慣れない値をすべてスローすると、無害なスキーマの追加が配信の失敗の繰り返しに変わります。
Polar Webhook ローカルホストの障害のトラブルシューティング
CLI は接続しますが、ルートは 404 を返します。
完全なローカル ルートを渡します polar listen、港だけではありません。 Next.js App Router で、ファイルの名前が次のとおりであることを確認します。 route.ts そして輸出 POST。ブラウザーの GET が 404 を返しても、POST ルートが欠落していることは証明されないため、次のようにテストします。 curl -X POST.
すべてのリクエストは 403 を返します
現在の CLI セッションによって出力されたシークレットがアプリに読み込まれていることを確認します。生の本文と 3 つの標準 Webhook ヘッダーすべてを保持します。汎用ライブラリを使用する場合は、Polar シークレットを 1 回だけ Base64 エンコードします。 Polar SDK ヘルパーを使用する場合は、元のシークレットを渡し、SDK にエンコードを処理させます。
チェックアウト後にイベントが表示されない
CLI が、アクションが発生したのと同じ組織および環境に接続されていることを確認します。リスナーターミナルを開いたままにし、サンドボックスリソースを使用し、アクションが実際にサブスクライブしたイベント状態に到達することを確認します。
イベントは 2 回処理されます
一意の制約を追加します webhook-id 重複の場合は成功を返します。状態の更新後、応答の前に例外が発生するかどうかを確認します。遅い副作用を outbox-backed ワーカーに移動します。
キャプチャされた古いリクエストは検証に失敗します
標準の Webhook 検証には、リプレイ攻撃を制限するためのタイムスタンプが含まれています。古いキャプチャは通常の検証ウィンドウで失敗するはずです。ビジネス ロジック テストの場合は、新しい配信を一度検証し、サニタイズされたペイロード フィクスチャを認証境界の背後に保存します。
生産前のセキュリティチェックリスト
- 個別のローカル、サンドボックス、実稼働エンドポイント シークレットを使用します。
- 処理する前に、生の本文、タイムスタンプ、配信 ID、署名を検証します。
- API トークンと Webhook シークレットをサーバー専用の環境変数に保持します。
- 重複排除の基準
webhook-id製品、組織、顧客の識別子を検証します。 - 顧客の電子メール、請求データ、メタデータ、および共有ログからの完全なペイロードを秘匿化します。
- リクエストサイズ制限を適用し、繰り返される無効な署名を監視します。
- ローカル リスナーを安定した HTTPS エンドポイントに置き換え、ライブ製品を有効にする前に新しいサンドボックス フローをテストします。
Polar CLI は展開ループを削除しますが、運用管理は削除しないでください。署名検証、冪等性、イベント フィルタリング、永続処理をローカルで有効にしておくことで、テストするコードが出荷されるコードになります。フレームワークに依存しない検証の詳細については、「 Webhook 署名検証ガイド そして一般的な ローカル デバッグ ワークフロー.
