Open46

読者コミュニティ|Next.jsとStripeではじめるシンプルなECサイト開発ワークショップ

Hidetaka Okamoto(Stripe)Hidetaka Okamoto(Stripe)

本の感想や質問をお気軽にコメントしてください。

https://zenn.dev/stripe/books/stripe-nextjs-use-shopping-cart

KaabeeKaabee

いつも有用な記事をありがとうございます。無事にECサイト作成できました!試しに、Netlifyのホスティングサービスを使用してデプロイしてみたいのですが、stripeAPIキーに関するエラーが発生します。本番環境へのデプロイは、stripeも本番環境に移行しないと出来ないのでしょうか?

Hidetaka Okamoto(Stripe)Hidetaka Okamoto(Stripe)

ローカル環境ではなく、NetlifyやVercel, firebaseなどへアプリをデプロイする場合は、それぞれのサービスの環境変数にStripeのAPIキーを設定する必要があります。
設定方法については、各サービスのドキュメントにてEnvironment Variablesなどで検索お願いします。
https://docs.netlify.com/

KaabeeKaabee

税金の設定についてですが、
あらかじめ商品価格を税込価格で作成した場合、商品ページの「税率」タブから税率の設定も必要でしょうか?
また、Stripe Taxという自動で税金を設定してくれるサービスに関して、こちらの記事を拝見させていただきましたが、このサービスは有料という認識で間違いないでしょうか?このサービスを利用しない場合は、「設定」-> 「税金設定」->「税金の自動計算」をオフに設定すれば良いですか?

Hidetaka Okamoto(Stripe)Hidetaka Okamoto(Stripe)

商品の料金を税込で設定している場合は、税率の設定やTaxの利用は必要ありません。
Stripe Taxは https://dashboard.stripe.com/tax で明示的に有効化しないと利用できない有料オプションです。ですので、ご自身で有効化操作をしていない場合は特に操作をする必要はありません。

KaabeeKaabee

度々すみません…
zapierと連携させてWebhookイベントのメール送信を自動化したいのですが、こちらはstripeのテスト環境下では出来ない仕様に変更されたようなのですが、本番環境で、実際の支払いを行わずにシミュレーションするには、どのように進めたら良いでしょうか。

KaabeeKaabee

ご回答ありがとうございます。pipedreamについて少しも知識がないので少し教えていただきたいのですが、pipedreamと zapierの違いとしてどのようなものがあるのでしょうか。pipedreamだと、stripeのテスト環境で使えるということでしょうか。

Hidetaka Okamoto(Stripe)Hidetaka Okamoto(Stripe)

あまり詳しいわけではないのですが、基本的にはZapierと変わりがないものだと思います。
個人的に利用している理由は、
・無料プランで多少JavaScriptのコードが書けること
・Stripeのテストモードでも利用できること
の2点です。

KaabeeKaabee

ありがとうございます。
もし、stripeとpipedreamとの連携を紹介したstripeコミュニティ等の記事や動画がありましたら、ぜひ共有いただきたいです。とりあえず、私も触ってみます。ありがとうございます。

KaabeeKaabee

ありがとうございます。試してみます!

あと、何度もすみません…
サブスク契約で、契約の更新時にstripeカスタマーポータルのURLを顧客にメール送信することによって、取引の履歴や契約キャンセルや一時停止を受け付けたいと考えているのですが、カスタマーポータルURLの有効期間は何日ですか。有効期間はこちらで変更も可能ですか。もし可能でない場合、自社で顧客情報を管理するデータベースは持たない方向なのですが、顧客が自由に自身のカスタマーポータルページにアクセスする手段として何か方法はありますでしょうか。

Hidetaka Okamoto(Stripe)Hidetaka Okamoto(Stripe)

カスタマーポータルは、APIでセッションを作成する場合、有効期限は非常に短く設定されており、変更もできません。
https://stripe.com/docs/customer-management/integrate-customer-portal#go-live

ノーコードの組み込み方法は、全ての顧客に同一URLが利用でき、有効期限はありません。
*ページアクセス時にStripe側が認証を行います。
https://stripe.com/docs/customer-management/activate-no-code-customer-portal

KaabeeKaabee

ありがとうございます。
ノーコードの場合のログインリンク共有についてですが、これは、契約更新時の顧客へのメールに添付しておいても問題ないでしょうか。全ての顧客に同一URLとなっていますが、メール認証を経ることによって、顧客ごとに別々のポータルページを割り振っているという感じですか。

KaabeeKaabee

ありがとうございます。

本当に何度もすみませんが、、、
あと、サブスクプランの契約日の設定についてですが、契約スタート可能な曜日または日付をあらかじめ設定しておくことは可能ですか。契約スタート日が注文日とは必ずしも一致しない場合はどのようにしたらよいでしょうか。よろしくお願いいたします。

Hidetaka Okamoto(Stripe)Hidetaka Okamoto(Stripe)

billing_cycle_anchorを設定することで請求開始日を指定できます。

https://stripe.com/docs/billing/subscriptions/billing-cycle

Subscription Schedule APIを利用して、初月のみクーポンを設定して割引のようなことも可能ではあります。

https://stripe.com/docs/billing/subscriptions/subscription-schedules

ワークショップ資料の内容から離れた内容になってきておりますので、
詳細等についてはサポートへご相談ください。

https://support.stripe.com/

hideamehideame

「04 Next.jsでAPIやサーバー側処理のあるサイトを作ってみよう」の最後の箇所で「npx next start -p 3100」を実行したところ、下記エラーが発生しました。どのようにすればエラーが回避できるでしょうか?

% npx next start -p 3100                                                                                                                  [main]:+
ready - started server on 0.0.0.0:3100, url: http://localhost:3100
Error: Could not find a production build in the '/Users/hamez/_pj/nextjs-stripe-ec/.next' directory. Try building your app with 'next build' before starting the production server. https://nextjs.org/docs/messages/production-start-no-build-id
    at NextNodeServer.getBuildId (/Users/hamez/_pj/nextjs-stripe-ec/node_modules/next/dist/server/next-server.js:139:23)
    at new Server (/Users/hamez/_pj/nextjs-stripe-ec/node_modules/next/dist/server/base-server.js:90:29)
    at new NextNodeServer (/Users/hamez/_pj/nextjs-stripe-ec/node_modules/next/dist/server/next-server.js:86:9)
    at NextServer.createServer (/Users/hamez/_pj/nextjs-stripe-ec/node_modules/next/dist/server/next.js:109:16)
    at async /Users/hamez/_pj/nextjs-stripe-ec/node_modules/next/dist/server/next.js:121:31
Hidetaka Okamoto(Stripe)Hidetaka Okamoto(Stripe)

Error: Could not find a production build in ~'.next' directory.については、npx next buildを実行してから再実行することで解決できます。

hideamehideame

ありがとうございます!「npx next build」でエラー無くなりました。

FuzzballFuzzball

06 の 「jqを利用して整形した実行結果の例」で、
curl http://localhost:3000/api/products | jq .
を実行したのですが、
zsh: command not found: jq
が帰ってきて商品一覧が表示されません。

hideamehideame

06の下記コマンドを実行したところ、下記エラーになりました。

% curl http://localhost:3000/api/products                                                                                                 [main]:+
wait  - compiling /api/products...
wait  - compiling...
event - compiled client and server successfully in 110 ms (426 modules)
error - Error: You did not provide an API key. You need to provide your API key in the Authorization header, using Bearer auth (e.g. 'Authorization: Bearer YOUR_SECRET_KEY'). See https://stripe.com/docs/api#authentication for details, or we can help at https://support.stripe.com/.
wait  - compiling /_error (client and server)...
wait  - compiling...
event - compiled client and server successfully in 87 ms (427 modules)
<!DOCTYPE html><html><head><style data-next-hide-fouc="true">body{display:none}</style><noscript data-next-hide-fouc="true"><style>body{display:block}</style></noscript><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills.js?ts=1650607714161"></script><script src="/_next/static/chunks/webpack.js?ts=1650607714161" defer=""></script><script src="/_next/static/chunks/main.js?ts=1650607714161" defer=""></script><script src="/_next/static/chunks/pages/_app.js?ts=1650607714161" defer=""></script><script src="/_next/static/chunks/pages/_error.js?ts=1650607714161" defer=""></script><script src="/_next/static/development/_buildManifest.js?ts=1650607714161" defer=""></script><script src="/_next/static/development/_ssgManifest.js?ts=1650607714161" defer=""></script><script src="/_next/static/development/_middlewareManifest.js?ts=1650607714161" defer=""></script><noscript id="__next_css__DO_NOT_USE__"></noscript></head><body><div id="__next"></div><script src="/_next/static/chunks/react-refresh.js?ts=1650607714161"></script><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":500}},"page":"/_error","query":{"__NEXT_PAGE":"/api/products"},"buildId":"development","isFallback":false,"err":{"name":"Error","message":"You did not provide an API key. You need to provide your API key in the Authorization header, using Bearer auth (e.g. 'Authorization: Bearer YOUR_SECRET_KEY'). See https://stripe.com/docs/api#authentication for details, or we can help at https://support.stripe.com/.","stack":"Error: You did not provide an API key. You need to provide your API key in the Authorization header, using Bearer auth (e.g. 'Authorization: Bearer YOUR_SECRET_KEY'). See https://stripe.com/docs/api#authentication for details, or we can help at https://support.stripe.com/.\n    at res.toJSON.then.StripeAPIError.message (/Users/hamez/_pj/nextjs-stripe-ec/node_modules/stripe/lib/StripeResource.js:214:23)\n    at processTicksAndRejections (node:internal/process/task_queues:96:5)"},"gip":true,"scriptLoader":[]}</script></body></html>%                                             

.env.local ファイルの作成場所等に問題がありますか。

% ls nextjs-stripe-ec/.env.local 
nextjs-stripe-ec/.env.local
Hidden comment
Hidetaka Okamoto(Stripe)Hidetaka Okamoto(Stripe)

環境変数をNext.jsが読み込めていない様子ですので、一度npx next devを停止して再実行していただけますでしょうか?

hideamehideame

「npx next start」が起動していたので、そちらを停止/再実行しましたが、同様のエラーが出力されているようです。

% jobs
[1]  - running    npm run dev
[2]  + running    npx next start -p 3100

# 上記「npx next start -p 3100」を停止

% npx next start -p 3100 &
[2] 12757

% curl http://localhost:3000/api/products
error - Error: You did not provide an API key. You need to provide your API key in the Authorization header, using Bearer auth (e.g. 'Authorization: Bearer YOUR_SECRET_KEY'). See https://stripe.com/docs/api#authentication for details, or we can help at https://support.stripe.com/.
<!DOCTYPE html><html><head><style data-next-hide-fouc="true">body{display:none}</style><noscript data-next-hide-fouc="true"><style>body{display:block}</style></noscript><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills.js?ts=1650608864866"></script><script src="/_next/static/chunks/webpack.js?ts=1650608864866" defer=""></script><script src="/_next/static/chunks/main.js?ts=1650608864866" defer=""></script><script src="/_next/static/chunks/pages/_app.js?ts=1650608864866" defer=""></script><script src="/_next/static/chunks/pages/_error.js?ts=1650608864866" defer=""></script><script src="/_next/static/development/_buildManifest.js?ts=1650608864866" defer=""></script><script src="/_next/static/development/_ssgManifest.js?ts=1650608864866" defer=""></script><script src="/_next/static/development/_middlewareManifest.js?ts=1650608864866" defer=""></script><noscript id="__next_css__DO_NOT_USE__"></noscript></head><body><div id="__next"></div><script src="/_next/static/chunks/react-refresh.js?ts=1650608864866"></script><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{"statusCode":500}},"page":"/_error","query":{"__NEXT_PAGE":"/api/products"},"buildId":"development","isFallback":false,"err":{"name":"Error","message":"You did not provide an API key. You need to provide your API key in the Authorization header, using Bearer auth (e.g. 'Authorization: Bearer YOUR_SECRET_KEY'). See https://stripe.com/docs/api#authentication for details, or we can help at https://support.stripe.com/.","stack":"Error: You did not provide an API key. You need to provide your API key in the Authorization header, using Bearer auth (e.g. 'Authorization: Bearer YOUR_SECRET_KEY'). See https://stripe.com/docs/api#authentication for details, or we can help at https://support.stripe.com/.\n    at res.toJSON.then.StripeAPIError.message (/Users/hamez/_pj/nextjs-stripe-ec/node_modules/stripe/lib/StripeResource.js:214:23)\n    at processTicksAndRejections (node:internal/process/task_queues:96:5)"},"gip":true,"scriptLoader":[]}</script></body></html>%
FuzzballFuzzball

09 の「APIをアプリに組み込もう」で

<form action="/api/checkout_session" method="POST">
                              <input type='hidden' name='price' value={price.id}/>
                              <input type='hidden' name='quantity' value={1}/> 
                              <Button type='submit'>いますぐ注文する</Button>
                            </form>

に書き換えたところ
「今すぐ注文する」のボタンを押すと

http://localhost:3000/api/checkout_session
に遷移し下記のように表示されてしまいます。

{"id":"cs_test_a1VepRRes1oytOVe7gv5kFO2s8Z3RCEgGhdItCwLSX5YqrBqsCyRWatVwc","object":"checkout.session","after_expiration":null,"allow_promotion_codes":null,"amount_subtotal":100,"amount_total":100,"automatic_tax":{"enabled":false,"status":null},"billing_address_collection":null,"cancel_url":"http://localhost:3000/","client_reference_id":null,"consent":null,"consent_collection":null,"currency":"jpy","customer":null,"customer_creation":"always","customer_details":null,"customer_email":null,"expires_at":1650696338,"livemode":false,"locale":null,"metadata":{},"mode":"payment","payment_intent":"pi_3KrGFLLfNcyBUaEb26Nfz19I","payment_link":null,"payment_method_options":{},"payment_method_types":["card"],"payment_status":"unpaid","phone_number_collection":{"enabled":false},"recovered_from":null,"setup_intent":null,"shipping":null,"shipping_address_collection":null,"shipping_options":[],"shipping_rate":null,"status":"open","submit_type":null,"subscription":null,"success_url":"http://localhost:3000/success","total_details":{"amount_discount":0,"amount_shipping":0,"amount_tax":0},"url":"https://checkout.stripe.com/pay/cs_test_a1VepRRes1oytOVe7gv5kFO2s8Z3RCEgGhdItCwLSX5YqrBqsCyRWatVwc#fidkdWxOYHwnPyd1blpxYHZxWjA0TndAYVdJY0tmfEdQZEBnMV9nMn8wTGxtcElxc3BRYzdpMHE0cW53aHNRMDE9MTVPSlFTU3NAZFw2Uk1tUH9kNV1VTjRuSXFNVmMwdkZMNFNwPEhNUjNWNTVCTUY0VTZOUScpJ2N3amhWYHdzYHcnP3F3cGApJ2lkfGpwcVF8dWAnPyd2bGtiaWBabHFgaCcpJ2BrZGdpYFVpZGZgbWppYWB3dic%2FcXdwYHgl"}
Hidetaka Okamoto(Stripe)Hidetaka Okamoto(Stripe)

「作成した決済ページにリダイレクトしよう」ステップのコード変更が保存できていないかもしれません。
データがそのまま表示されるのは、res.status(200).json(session)が実行されているからの可能性が高いです。
res.redirect(301, session.url)で保存できていればリダイレクトしますので、一度確認をお願いします。

https://zenn.dev/stripe/books/stripe-nextjs-use-shopping-cart/viewer/step4-2_checkout_session#作成した決済ページにリダイレクトしよう

yu11yu11

貴重な情報ありがとうございます!
全て実装してSafariやiPhoneのGoogleで会計(checkout_session)に移るとポップアップのブロックで決済画面に遷移できません。こちら対策はございますか👀??

Hidetaka Okamoto(Stripe)Hidetaka Okamoto(Stripe)

Google Payでの決済処理がうまくいかない・・・という理解で良いでしょうか?

「Checkoutページの表示がうまくいかない」ケースについてですと、
window.open(session.url)の処理がブロッカーに止められているのかなと思います。
この挙動自体はブラウザの仕様によるものですので、ユーザー側で許可していただく必要があります。

回避する方法としては、Next.jsのuseRouterを使って、同じウィンドウ内で遷移させればよいと思います。

https://nextjs-ja-translation-docs.vercel.app/docs/api-reference/next/router#routerpush

yu11yu11

認識のとおりです。ご回答ありがとうございます!
userouterで対策できました!

yu11yu11

すみません、追加で。。
cart_sessionで決済終了後、successページに遷移するかと思いますが、
カートの中身がリセットされていません。
こちらの対策をご教示頂けますか??
(今は暫定的にカード情報等を入力する画面に遷移する時にclearCart()しています!

Hidetaka Okamoto(Stripe)Hidetaka Okamoto(Stripe)

注文完了後の後処理についてが、本資料には抜けていますね・・・
後ほど章の追加をしますので、詳細については少々お待ちください。

簡単に流れを書きますと、以下の通りです。
・Next.jsで注文完了用ページを用意(page/thanks.tsxなど)
<CartProvider />successUrl に作成したthanksページのURLを指定
・注文完了ページにて、useEffectを使ってclearCart()を実行

リダイレクト前にクリアしますと、カゴ落ち時にカートデータが復元できなくなります。
そのため、注文完了ページ側で処理することをお勧めします。

注意点

コンビニ決済・銀行振込をサポートする場合、リダイレクトされないためにこの動きは利用できません。
もしコンビニ決済・銀行振込を利用する場合は、今暫定対応として実装されている「リダイレクト前にクリアする」での対応となります。

yu11yu11

ご丁寧に回答頂きありがとうございます。
確かに、successUrlでclearCart()しなければなりませんね!
コンビニ・銀行振り込みについても承知いたしました!

yu11yu11

すみません、下記についてご教示願いたいです。
・併せて、決済完了後に商品を非表示するにはどうすればよいか。
アーカイブをAPI側で操作(active: false)ですればよいとドキュメントにありましたがうまく行かず、ご教示頂きたいです。
よろしくお願いします。

Hidetaka Okamoto(Stripe)Hidetaka Okamoto(Stripe)

うまくいかなかったケースですが、どのようなエラーが画面またはログに表示されていますでしょうか?

yu11yu11

お返事頂きありがとうございます。
checkout_sessionを呼び出す際に下記のエラーが発生しております。

error - unhandledRejection: Error: The provided key 'rk_test_*********************************************************************************************3x3N4t' does not have the required permissions for this endpoint on account 'acct_1LgMwAGdTYrk9Kye'. Having the 'rak_product_write' permission would allow this request to continue.

api/checkout_session.js(一部抜粋)

    const stripe = new Stripe(process.env.STRIPE_API_KEY, {
      apiVersion: '2022-08-01',
      maxNetworkRetries: 3,
    });
    const session = await stripe.checkout.sessions.create({
      mode: 'payment',
      line_items: lineItems,
      success_url: `${process.env.NEXT_PUBLIC_BASE_URL}/success`,
      cancel_url: `${process.env.NEXT_PUBLIC_BASE_URL}`,
      billing_address_collection: 'required',
    });
    items.map(async (item) => {
      await stripe.products.update(item.id, { active: false });
    });
Hidetaka Okamoto(Stripe)Hidetaka Okamoto(Stripe)

共有ありがとうございます。
APIエラーについてですが、制限付きキーに「productの書き込み権限」が付与されていないことが原因です。
ワークショップ資料では、必要最低限の権限のみ設定しておりますので、追加でAPI操作を行いたい場合は、そのリソースの書き込み・読み込み権限が有効になっているかの確認をお願いします。

yu11yu11

設定を変更することで解決できました。
(エラー文に解決策書いてましたね、すみません)。
ありがとうございました!

yu11yu11

いつもご対応頂きありがとうございます。
クレジット決済後に商品を表示させないようにしたいのですが、決済が完了した後のアクションはどこに記載していけばよろしいのでしょうか。

Hidden comment