「切り替えは5分で終わる」
そう思っていた。.envのAPIキーを差し替えてサービスを再起動するだけだ。何も難しいことはない。
実際には3時間かかった。
テストモードと本番モードの違い
Stripeにはテストモードとライブモードがある。テストモードではダミーカードで決済できる。ライブモードでは実際のカードで実際のお金が動く。
APIキーはテストモードと本番モードで別々に発行される。テストはsk_test_始まり、本番はsk_live_始まりだ。
切り替え手順はシンプルに見えた。.envファイルのAPIキーを本番用に差し替えて、再起動する。
Price IDが一致しない
最初のエラーはすぐに発生した。購入ボタンを押すと、Stripeが「price not found」エラーを返した。
テストモードで作成したPrice IDは本番モードでは使えない。テスト環境と本番環境は完全に別物だ。本番モードのダッシュボードで商品と価格を再作成して、新しいPrice IDをDBと.envに反映し直す必要があった。
設定すべき価格は3種類ある。巻単体(500円×5巻分)・全巻セット(1,800円)・月額サブスク(980円)。テストで一度やったことをもう一度やり直した。
Webhookの署名が検証できない
Price IDを修正して決済が通るようになったが、今度はWebhookが届かなくなった。
原因はWebhookのシークレット(STRIPE_WEBHOOK_SECRET)がテスト用のままだったことだ。StripeのWebhookシークレットはエンドポイントごとに発行される。本番モードのダッシュボードでエンドポイントを登録し直して、新しいシークレットを.envに設定した。
stripe.Webhook.construct_event(payload, sig_header, STRIPE_WEBHOOK_SECRET)
シークレットが一致しないと、このconstruct_eventが例外を投げる。ログには「Webhook signature verification failed」というエラーが出ていた。シークレットを差し替えて解消した。
顧客IDが毎回新しく作られる
3つ目の問題は購入フローを繰り返したときに発覚した。同じメールアドレスで2回購入すると、Stripeに2つの別々の顧客(Customer)が作られる。
決済セッション作成時に既存顧客を検索していなかったのが原因だ。
session = stripe.checkout.Session.create(
customer_email=email, # emailを渡すだけ→毎回新規顧客が作られる
payment_method_types=['card'],
...
)
理想的には「メールアドレスで既存顧客を検索→あれば使う・なければ作る」というフローだ。しかし本番切り替え直後の対応として「仕様として許容」する判断をした。
同じメールアドレスで複数の顧客が作られても、アクセス制御はStripe Session IDとメールアドレスで照合しているため、機能上の問題は起きない。ダッシュボードが少し散らかる程度だ。
本番切り替えチェックリスト
この経験から、Stripeの本番切り替え時に確認すべき項目をまとめた。
- APIキー(sk_live_ / pk_live_)を.envに設定済みか
- 本番ダッシュボードで商品・価格を作成済みか
- 新しいPrice IDをDBと.envに反映済みか
- 本番Webhookエンドポイントを登録済みか
- 新しいWebhookシークレット(whsec_live_...)を.envに設定済みか
この5点を事前に確認しておけば、切り替え当日の混乱は防げた。次のプロジェクトでStripeを使うとき、この一覧が役に立つはずだ。
*次回は「Stripeのサブスクを5回テストして学んだこと」*
*シンプルシステム株式会社 代表 伊藤勝彦*