【トラブルシューティング】Stripe SDK を Cloudflare Workers で動かす
トラブルシューティング約5分で読めます

この記事でわかること
- Workers で Stripe SDK が接続エラーになる原因(Node の http モジュールに依存している)
- Stripe.createFetchHttpClient() を渡すだけで直るという解決策
- Webhook の署名検証で Workers だと constructEventAsync が必要な理由
- 検証にはパース前の生ボディが要るという落とし穴
Stripe SDK v12 以上、Cloudflare Workers のデプロイ環境、Stripe アカウント。
メモ
Stripe SDK を Cloudflare Workers で使うと接続エラーが発生します。
Stripe.createFetchHttpClient() で Fetch API ベースに切り替える解決策と、Webhook 設定までの全フローを解説します。はじめに
Stripe Checkout でサブスクリプション決済を実装しているアプリを Cloudflare Workers に移行したところ、Stripe API への接続でエラーが発生しました。原因と解決策を共有します。
1. エラーの内容
Workers 上で Stripe Checkout Session を作成すると、以下のエラーが発生します。
An error occurred with our connection to Stripe.
Request was retried 2 times.原因
Stripe SDK はデフォルトで Node.js の http/https モジュールを使って HTTP リクエストを送信します。Cloudflare Workers にはこれらのモジュールが存在しないため、接続エラーになります。
2. 解決策: Fetch API ベースの HTTP クライアント
Stripe SDK に内蔵されている Fetch API ベースのクライアントを使用します。
import Stripe from "stripe";
if (!process.env.STRIPE_SECRET_KEY) {
throw new Error("STRIPE_SECRET_KEY is not set");
}
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
typescript: true,
httpClient: Stripe.createFetchHttpClient(),
});Tips
Stripe.createFetchHttpClient() は Stripe SDK v12+ に内蔵されている Fetch API ベースのクライアントです。Workers の Fetch API と完全互換で、この 1 行を追加するだけで解決します。3. Workers での Webhook 設定
Stripe CLI で Webhook エンドポイントを作成します。
# Webhook エンドポイント作成
stripe webhook_endpoints create \
--url https://your-app.workers.dev/api/stripe/webhook \
--enabled-events checkout.session.completed,customer.subscription.updated,customer.subscription.deletedWebhook Secret を Workers の環境変数に設定します。
npx wrangler secret put STRIPE_WEBHOOK_SECRET署名検証は constructEventAsync を使う
Webhook の受け口では、届いたリクエストが本当に Stripe からのものかを署名で確かめます。ここにも Workers 固有の落とし穴があります。
const body = await request.text(); // 生の文字列で受ける
const signature = request.headers.get("stripe-signature")!;
// Workers では同期版の constructEvent は使えない。
// 内部で Node の crypto を同期呼び出ししているため。
const event = await stripe.webhooks.constructEventAsync(
body,
signature,
process.env.STRIPE_WEBHOOK_SECRET!,
);重要
署名検証を省くと、誰でも「決済が完了した」というリクエストを投げられます。Webhook の URL は外部に公開されているので、ここを信用すると無料で pro プランになれます。また、検証にはパース前の生のボディが必要です。
request.json() で受けてから文字列に戻すと、バイト列が変わって検証に失敗します。4. 検証したフロー
以下の全フローが Workers 上で動作することを確認しました。
/api/stripe/checkout→ Stripe 決済ページにリダイレクト- テストカード(4242 4242 4242 4242)で決済完了
- Webhook で
checkout.session.completedイベント受信 - DB のユーザープランが
proに更新 /api/stripe/portal→ Customer Portal でプラン管理
Tips
Tips
Stripe のエラーメッセージは「connection to Stripe」としか出ないため、原因特定が難しいです。デバッグ時はレスポンスボディにエラー詳細を一時的に含めると原因特定しやすいです。ただし消し忘れに注意。本番で内部エラーをそのまま返すと、スタックトレースやキーの一部が外に出ることがあります。
wrangler tail でログを見る方が安全です。Tips
Workers Secret の設定は
wrangler secret put コマンドで行います。wrangler.jsonc には書かないでください(リポジトリに入ってしまいます)。参考リンク
- Stripe: Webhook の署名検証
- Cloudflare Workers での Stripe 利用
- Next.js アプリを OpenNext で Cloudflare Workers にデプロイする(別記事)
まとめ
- Stripe SDK はデフォルトで Node.js
httpを使うため Workers でエラー Stripe.createFetchHttpClient()で Fetch API ベースに切り替えて解決- Webhook Secret は
wrangler secret putで設定 - エラーメッセージが曖昧なので、デバッグ時はレスポンスに詳細を含めるのがコツ
更新履歴
- Webhook の署名検証の節を追加(Workers では同期版の constructEvent が使えず constructEventAsync が必要であること、検証には生のボディが必要であること、検証を省くと誰でも決済完了を偽装できること)。デバッグ時にエラー詳細をレスポンスに含める Tips に、本番での消し忘れのリスクを追記。コードブロックの言語指定を plain text から text に修正し、ファイルパスのキャプションを追加。callout の色指定を他記事と揃えた。参考リンクの節を新設。「この記事でわかること」「対象読者」「前提条件」を概要プロパティへ移動。


