# ラインアイテムを動的に更新する 決済時に行われた変更に応じてラインアイテムを更新します。 # 完全ホスト型ページ > This is a 完全ホスト型ページ for when payment-ui is stripe-hosted. View the full page at https://docs.stripe.com/payments/checkout/dynamically-update-line-items?payment-ui=stripe-hosted. [Checkout セッション](https://docs.stripe.com/api/checkout/sessions/object.md) に含まれる明細項目を動的に追加、削除、または更新します。 これにより以下が可能になります。 - **在庫の確認**:在庫チェックを実行し、顧客が商品の数量を変更しようとしたときに選択を保留します。 - **Add new products (新規商品を追加)**: 注文総額が特定の金額を超えている場合に、補完商品を追加します。 - **配送料金の更新**:注文合計が変更された場合、このガイドで説明されている方法と、[決済時の配送オプションのカスタマイズ](https://docs.stripe.com/payments/checkout/custom-shipping-options.md) の方法を組み合わせて配送料金を更新します。 - **Update tax rates (税率を更新)**: [Stripe Tax](https://docs.stripe.com/tax/checkout.md) を使用していない場合は、入力された配送先住所に基づいてラインアイテムの[税率](https://docs.stripe.com/billing/taxes/collect-taxes.md?tax-calculation=tax-rates#adding-tax-rates-to-checkout)を動的に更新できます。 動的な更新は、オンラインページではサポートされていません。この機能は [Elements with the Checkout Sessions API](https://docs.stripe.com/payments/advanced/dynamically-update-line-items.md) で使用できます。 # 完全埋め込みページ > This is a 完全埋め込みページ for when payment-ui is embedded-page. View the full page at https://docs.stripe.com/payments/checkout/dynamically-update-line-items?payment-ui=embedded-page. [Checkout セッション](https://docs.stripe.com/api/checkout/sessions/object.md) に含まれる明細項目を動的に追加、削除、または更新します。 これにより以下が可能になります。 - **在庫の確認**:在庫チェックを実行し、顧客が商品の数量を変更しようとしたときに選択を保留します。 - **Add new products (新規商品を追加)**: 注文総額が特定の金額を超えている場合に、補完商品を追加します。 - **配送料金の更新**:注文合計が変更された場合、このガイドで説明されている方法と、[決済時の配送オプションのカスタマイズ](https://docs.stripe.com/payments/checkout/custom-shipping-options.md) の方法を組み合わせて配送料金を更新します。 - **Update tax rates (税率を更新)**: [Stripe Tax](https://docs.stripe.com/tax/checkout.md) を使用していない場合は、入力された配送先住所に基づいてラインアイテムの[税率](https://docs.stripe.com/billing/taxes/collect-taxes.md?tax-calculation=tax-rates#adding-tax-rates-to-checkout)を動的に更新できます。 組み込みフォームでこの機能を使用できるよう対応中です。 この機能がリリースされた際に通知を受け取るか、代わりに [Checkout Sessions API の Elements](https://docs.stripe.com/payments/advanced/dynamically-update-line-items.md) でこの機能を利用してください。 # 埋め込みフォーム > This is a 埋め込みフォーム for when payment-ui is checkout-form. View the full page at https://docs.stripe.com/payments/checkout/dynamically-update-line-items?payment-ui=checkout-form. > [組み込みフォームの連携](https://docs.stripe.com/payments/checkout/how-checkout-works.md?payment-ui=checkout-form)について、を表示。 [Checkout Session (セッション)](https://docs.stripe.com/api/checkout/sessions/object.md) に含まれる項目を動的に追加、削除、更新する方法をご紹介します。 ### ご利用事例 このガイドでは、項目を更新してサブスクリプションをアップセルする方法を示しますが、以下を行うこともできます。 - **在庫の確認**:在庫チェックを実行し、顧客がアイテムの数量を変更しようとしたときに保留します。 - **新しい商品を追加する**:注文合計額が一定の金額を超えている場合に、補完商品を追加します。 - **配送料金を更新する**:注文合計額が変更された場合は、このガイドの[購入時に配送オプションをカスタマイズする](https://docs.stripe.com/payments/checkout/custom-shipping-options.md)で説明されている方法を組み合わせて、配送料金を更新します。 - **税率を更新する**:[Stripe Tax](https://docs.stripe.com/tax/checkout.md) を使用していない場合は、入力された配送先住所に基づいて項目の[税率](https://docs.stripe.com/billing/taxes/collect-taxes.md?tax-calculation=tax-rates#adding-tax-rates-to-checkout)を動的に更新できます。 ## Checkout セッションを作成 [サーバー側] サーバーで *Checkout セッション* (A Checkout Session represents your customer's session as they pay for one-time purchases or subscriptions through Checkout. After a successful payment, the Checkout Session contains a reference to the Customer, and either the successful PaymentIntent or an active Subscription)を作成します。 > この機能を使うには、SDK のバージョンが `2025-03-31.basil` 降であることを確認してください。 ```curl curl https://api.stripe.com/v1/checkout/sessions \ -u "<>:" \ -H "Stripe-Version: 2026-05-27.preview; custom_checkout_payment_form_preview=v1" \ -d ui_mode=form \ -d mode=subscription \ -d "line_items[0][price]={{PRICE_ID}}" \ -d "line_items[0][quantity]=1" \ --data-urlencode "return_url=https://example.com/return" ``` ## ラインアイテムを動的に更新する [サーバー側] サーバーでエンドポイントを作成し、Checkout Session の項目を更新します。これは、後のステップでフロントエンドから呼び出します。 > クライアント側のコードは、ユーザーによって制御された環境で実行されます。悪意のあるユーザーは、クライアント側の検証を迂回し、リクエストを傍受して変更したり、サーバーへの新しいリクエストを作成したりすることができます。 エンドポイントを作成する際には、次のことを推奨します。 - 汎用的にするのではなく、特定の顧客とのやり取りのためにエンドポイントを作成します。たとえば、一般的な「更新」アクションの代わりに「クロスセル項目を追加」します。特定のエンドポイントは、検証ロジックの作成と維持に役立ちます。 - クライアントからエンドポイントに [セッションデータ](https://docs.stripe.com/js/custom_checkout/session_object) を直接渡さないでください。悪意のあるクライアントはリクエストデータを変更し、Checkout Session の状態を判別するための信頼できないソースにする可能性があります。代わりに、[セッション ID](https://docs.stripe.com/js/custom_checkout/session_object#custom_checkout_session_object-id) をサーバーに渡して、それを使用して Stripe API からデータを安全に取得してください。 #### Ruby ```ruby require 'sinatra' require 'json' require 'stripe' set :port, 4242 # Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. # Find your keys at https://dashboard.stripe.com/apikeys. client = Stripe::StripeClient.new( '<>', stripe_version: '2026-05-27.dahlia; custom_checkout_payment_form_preview=v1;', ) MONTHLY_PRICE_ID = '{{MONTHLY_PRICE}}' YEARLY_PRICE_ID = '{{YEARLY_PRICE}}' post '/change-subscription-interval' do content_type :json request.body.rewind request_data = JSON.parse(request.body.read) checkout_session_id = request_data['checkout_session_id'] interval = request_data['interval'] if checkout_session_id.nil? || !['yearly', 'monthly'].include?(interval) status 400 return { type: 'error', message: "We couldn't process your request. Please try again later." }.to_json end begin # 1. Create the new line items for the Checkout Session. new_price = interval == 'yearly' ? YEARLY_PRICE_ID : MONTHLY_PRICE_ID line_items = [{ price: new_price, quantity: 1, }] # 2. Update the Checkout Session with the new line items. client.v1.checkout.sessions.update(checkout_session_id, { line_items: line_items, }) # 3. Return a success response. { type: 'success' }.to_json rescue Stripe::StripeError # Handle Stripe errors with a generic error message status 400 { type: 'error', message: "We couldn't process your request. Please try again later." }.to_json rescue StandardError # Handle unexpected errors status 500 { type: 'error', message: 'Something went wrong on our end. Please try again later.' }.to_json end end ``` 項目を更新するときは、項目の配列全体を再送信する必要があります。 - 既存の項目を維持するには、その `id` を指定します。 - 既存の項目を更新するには、更新するフィールドの新しい値とともにその `id` を指定します。 - 新しい項目を追加するには、`id` なしで `price` と `quantity` を指定します。 - 既存の項目を削除するには、再送信された配列から項目の ID を省略します。 - 項目の順序を変更するには、再送信された配列内の目的の位置にその `id` を指定します。 ## クライアント SDK を更新 [クライアント側] #### HTML + JS Stripe.js を初期化。 ```javascript const stripe = Stripe('<>', { betas: ['custom_checkout_payment_form_1'] }); ``` #### React `stripe` インスタンスを初期化。 ```javascript import {loadStripe} from '@stripe/stripe-js'; const stripe = loadStripe("<>", { betas: ['custom_checkout_payment_form_1'] }); ``` ## サーバー更新リクエスト [クライアント側] #### HTML + JS フロントエンドからサーバーに更新リクエストを送信する機能を作成し、[runServerUpdate](https://docs.stripe.com/js/custom_checkout/run_server_update) でラップします。リクエストが成功すると、新しい項目で [Session](https://docs.stripe.com/js/custom_checkout/session_object) オブジェクトが更新されます。 `runServerUpdate` は、更新機能に 20 秒のタイムアウトを適用します。機能が 20 秒以内に解決されない場合、`runServerUpdate` はエラーを返します。エラーを処理するために `try`/`catch` ブロックで `runServerUpdate` コールをラップし、タイムアウトやその他の失敗を診断するためのメトリクスを記録します。 `runServerUpdate` の呼び出しを `try` / `catch` ブロックで囲み、サーバーおよび `runServerUpdate` 自体からのエラー (タイムアウトなど) を処理します。`response.type === 'error'` は、Stripe が更新済みセッションを内部で取得する際の失敗を対象とします。サーバー側で返されたエラー (4xx や 5xx レスポンスなど) は `response` に反映されません。[Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) はステータスコードに関係なく、完了した HTTP レスポンスに対して解決するためです。更新関数内で `response.ok` を確認し、失敗時にスローすることで `catch` ブロックに到達させます。 ```html ``` ```javascript document.getElementById('change-subscription-interval') .addEventListener("click", async (event) => { const button = event.target; const isCurrentSubscriptionMonthly = button.getAttribute("aria-checked") === "false"; const updateCheckout = async () => { const response = await fetch("/change-subscription-interval", { method: "POST", headers: { "Content-type": "application/json", }, body: JSON.stringify({ checkout_session_id: actions.getSession().id, interval: isCurrentSubscriptionMonthly ? "yearly" : "monthly", }) }); if (!response.ok) { const body = await response.json(); throw new Error(body.message); } }; try {const response = await checkout.runServerUpdate(updateCheckout); if (response.type === 'error') { // Handle Stripe API errors (for example, session retrieval failure) return; } } catch (error) { // Handle promise rejection from your server (4xx/5xx errors) or // from runServerUpdate itself (for example, timeouts). // error.message contains the message thrown from your update function. return; } // Update toggle state on success const isNewSubscriptionMonthly = !isCurrentSubscriptionMonthly; button.setAttribute("aria-checked", !isNewSubscriptionMonthly); button.textContent = isNewSubscriptionMonthly ? "Save with a yearly subscription" : "Use monthly subscription"; }); ``` #### React フロントエンドからサーバーに更新リクエストを送信し、[runServerUpdate](https://docs.stripe.com/js/custom_checkout/run_server_update) でラップします。リクエストが成功すると、[Session](https://docs.stripe.com/js/custom_checkout/session_object) オブジェクトが新しい項目で更新されます。 `runServerUpdate` は、更新機能に 20 秒のタイムアウトを適用します。機能が 20 秒以内に解決されない場合、`runServerUpdate` はエラーを返します。エラーを処理するために `try`/`catch` ブロックで `runServerUpdate` コールをラップし、タイムアウトやその他の失敗を診断するためのメトリクスを記録します。 `runServerUpdate` の呼び出しを `try` / `catch` ブロックで囲み、サーバーおよび `runServerUpdate` 自体からのエラー (タイムアウトなど) を処理します。`response.type === 'error'` は、Stripe が更新済みセッションを内部で取得する際の失敗を対象とします。サーバー側で返されたエラー (4xx や 5xx レスポンスなど) は `response` に反映されません。[Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) はステータスコードに関係なく、完了した HTTP レスポンスに対して解決するためです。更新関数内で `response.ok` を確認し、失敗時にスローすることで `catch` ブロックに到達させます。 ```jsx import React from 'react'; import {useCheckoutElements} from '@stripe/react-stripe-js/checkout'; const ChangeSubscriptionInterval = () => { const [isSubscriptionMonthly, setIsSubscriptionMonthly] = React.useState(false); const checkoutState = useCheckoutElements(); if (checkoutState.type === 'loading') { return (
Loading...
); } else if (checkoutState.type === 'error') { return (
Error: {checkoutState.error.message}
); } let actions = null; const loadActionsResult = await checkoutState.loadActions(); if (loadActionsResult.type === 'success') { actions = loadActionsResult.actions; } else { return (
Error: {loadActionsResult.error.message}
); } const updateCheckout = async () => { const response = await fetch("/change-subscription-interval", { method: "POST", headers: { 'Content-type': 'application/json', }, body: JSON.stringify({ checkout_session_id: actions?.getSession()?.id, interval: isSubscriptionMonthly ? 'yearly' : 'monthly' }) }); if (!response.ok) { const body = await response.json(); throw new Error(body.message); } }; const handleClick = async () => { try {const response = await actions?.runServerUpdate(updateCheckout); if (response.type === 'error') { // Handle Stripe API errors (for example, session retrieval failure) return; } } catch (error) { // Handle promise rejection from your server (4xx/5xx errors) or // from runServerUpdate itself (for example, timeouts). // error.message contains the message thrown from your update function. return; } // Update toggle state on success setIsSubscriptionMonthly(!isSubscriptionMonthly); }; return ( ); }; ```