# 配送オプションを動的にカスタマイズする 顧客の配送先住所に基づいて配送オプションを更新します。 # Hosted page > This is a Hosted page for when payment-ui is stripe-hosted. View the full page at https://docs.stripe.com/payments/checkout/custom-shipping-options?payment-ui=stripe-hosted. The hosted page integration doesn’t support dynamically customizing shipping options. To dynamically customize shipping options, use the [Embedded page](https://docs.stripe.com/payments/checkout/custom-shipping-options.md?payment-ui=embedded-form) or the [Checkout elements](https://docs.stripe.com/payments/checkout/custom-shipping-options.md?payment-ui=embedded-components) integration instead. # Embedded page > This is a Embedded page for when payment-ui is embedded-form. View the full page at https://docs.stripe.com/payments/checkout/custom-shipping-options?payment-ui=embedded-form. Learn how to dynamically update shipping options based on the address that your customer enters when you use Checkout. ### ご利用事例 - **住所を検証する**: 貴社に合わせたカスタム検証ルールを使用して、顧客の住所に商品を配送できるかどうかを確認します。また、顧客が希望する住所を確認するためのカスタム UI を作成することもできます。 - **関連する配送オプションを表示する**: 顧客の住所に基づいて、利用可能な配送方法のみを表示します。たとえば、お住まいの国での配送の場合にのみ翌日配送を表示するなどです。 - **配送料を動的に計算する**: 顧客の配送先住所に基づいて配送料を計算して表示します。 - **注文合計額に基づいて配送料を更新する**: 配送先住所または注文合計額に基づいて配送料を提示します (100 USD を超える注文の場合は送料無料など)。数量変更やクロスセルが可能な決済フローについては、[項目を動的に更新する](https://docs.stripe.com/payments/checkout/dynamically-update-line-items.md)をご覧ください。 ### 制限事項 - [payment mode (支払いモード)](https://docs.stripe.com/api/checkout/sessions/object.md#checkout_session_object-mode) でのみサポートされます。[shipping rates (配送料金)](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-shipping_options) はサブスクリプションモードではご利用いただけません。 - 決済ページの外部からの変更に対応した更新はサポートしていません。 ## 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)を作成します。 - [ui_mode](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-ui_mode) を `embedded` に設定します。 - [permissions.update_shipping_details](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-permissions-update_shipping_details) を `server_only` に設定します。 - [shipping_address_collection.allowed_countries](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-shipping_address_collection-allowed_countries) に、配送先を提供する国のリストを設定します。 - [shipping_options.shipping_rate_data](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-shipping_options-shipping_rate_data) を設定して、0 USD のダミー配送料を作成します。 デフォルトでは、Stripe Checkout クライアントは、[Checkout セッション](https://docs.stripe.com/api/checkout/sessions/object.md)オブジェクトの [shipping_details](https://docs.stripe.com/api/checkout/sessions/object.md#checkout_session_object-collected_information-shipping_details) を、顧客が入力する顧客の[名前](https://docs.stripe.com/api/checkout/sessions/update.md#update_checkout_session-collected_information-shipping_details-name)や[住所](https://docs.stripe.com/api/checkout/sessions/update.md#update_checkout_session-collected_information-shipping_details-address)などの配送情報で自動的に更新します。 > [permissions.update_shipping_details](https://docs.stripe.com/api/checkout/sessions/create.md#create_checkout_session-permissions-update_shipping_details) が `server_only` の場合、クライアント側の自動更新は無効になり、サーバーのみが Stripe の秘密キーを使用して配送情報を更新できます。 ```curl curl https://api.stripe.com/v1/checkout/sessions \ -u "<>:" \ -d ui_mode=embedded \ -d "permissions[update_shipping_details]"=server_only \ -d "shipping_address_collection[allowed_countries][0]"=US \ -d "shipping_options[0][shipping_rate_data][display_name]"="Dummy shipping" \ -d "shipping_options[0][shipping_rate_data][type]"=fixed_amount \ -d "shipping_options[0][shipping_rate_data][fixed_amount][amount]"=0 \ -d "shipping_options[0][shipping_rate_data][fixed_amount][currency]"=usd \ -d "line_items[0][price]"={{PRICE_ID}} \ -d "line_items[0][quantity]"=1 \ -d mode=payment \ --data-urlencode return_url="https://example.com/return" ``` ## 配送オプションをカスタマイズする [サーバー側] サーバーで、顧客の配送先住所に基づいて配送オプションを計算する新しいエンドポイントを作成します。 1. リクエスト本文から取得した `checkoutSessionId` を使用して、[Checkout セッション](https://docs.stripe.com/api/checkout/sessions/object.md)を[取得](https://docs.stripe.com/api/checkout/sessions/retrieve.md)します。 1. リクエスト本文にある顧客の配送先情報を検証します。 1. 顧客の配送先住所と [Checkout セッション](https://docs.stripe.com/api/checkout/sessions/object.md)のラインアイテムに基づいて配送オプションを計算します。 1. 顧客の [shipping_details](https://docs.stripe.com/api/checkout/sessions/object.md#checkout_session_object-collected_information-shipping_details) と [shipping_options](https://docs.stripe.com/api/checkout/sessions/update.md#update_checkout_session-shipping_options) で [Checkout セッション](https://docs.stripe.com/api/checkout/sessions/object.md)を[更新](https://docs.stripe.com/api/checkout/sessions/update.md)します。 #### Ruby ```ruby require 'sinatra' require 'json' require 'stripe' set :port, 4242 # Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/apikeys Stripe.api_key = "<>" # Return a boolean indicating whether the shipping details are valid def validate_shipping_details(shipping_details) # TODO: Remove error and implement... raise NotImplementedError.new(<<~MSG) Validate the shipping details the customer has entered. MSG end # Return an array of the updated shipping options or the original options if no update is needed def calculate_shipping_options(shipping_details, session) # TODO: Remove error and implement... raise NotImplementedError.new(<<~MSG) Calculate shipping options based on the customer's shipping details and the Checkout Session's line items. MSG end post '/calculate-shipping-options' do content_type :json request.body.rewind request_data = JSON.parse(request.body.read) checkout_session_id = request_data['checkout_session_id'] shipping_details = request_data['shipping_details'] # 1. Retrieve the Checkout Session session = Stripe::Checkout::Session.retrieve(checkout_session_id) # 2. Validate the shipping details if !validate_shipping_details(shipping_details) return { type: 'error', message: "We can't ship to your address. Please choose a different address." }.to_json end # 3. Calculate the shipping options shipping_options = calculate_shipping_options(shipping_details, session) # 4. Update the Checkout Session with the customer's shipping details and shipping options if shipping_options Stripe::Checkout::Session.update(checkout_session_id, { collected_information: { shipping_details: shipping_details }, shipping_options: shipping_options }) return { type: 'object', value: { succeeded: true } }.to_json else return { type: 'error', message: "We can't find shipping options. Please try again." }.to_json end end ``` ## Checkout をマウントする [クライアント側] #### HTML + JS Checkout は [Stripe.js](https://docs.stripe.com/js.md) の一部として利用できます。HTML ファイルのヘッダーに Stripe.js スクリプトを追加してページに含めます。次に、マウンティングに使用する空の DOM ノード (コンテナ) を作成します。 ```html
``` 公開可能な API キーで Stripe.js を初期化します。 ```javascript // Set your publishable key: remember to change this to your live publishable key in production // See your keys here: https://dashboard.stripe.com/apikeys const stripe = Stripe('<>'); ``` サーバーに [Checkout セッション](https://docs.stripe.com/api/checkout/sessions/object.md)の作成をリクエストする非同期の `fetchClientSecret` 関数を作成し、クライアントシークレットを取得します。 顧客の配送先住所に基づいて配送オプションを計算するようにサーバーへリクエストする、非同期の `onShippingDetailsChange` 関数を作成します。顧客が配送先情報フォームの入力を終えると、Stripe Checkout がこの関数を呼び出します。 ```javascript initialize(); async function initialize() { // Fetch Checkout Session and retrieve the client secret const fetchClientSecret = async () => { const response = await fetch("/create-checkout-session", { method: "POST", }); const { clientSecret } = await response.json(); return clientSecret; }; // Call your backend to set shipping options const onShippingDetailsChange = async (shippingDetailsChangeEvent) => { const {checkoutSessionId, shippingDetails} = shippingDetailsChangeEvent; const response = await fetch("/calculate-shipping-options", { method: "POST", body: JSON.stringify({ checkout_session_id: checkoutSessionId, shipping_details: shippingDetails, }) }) if (response.type === 'error') { return Promise.resolve({type: "reject", errorMessage: response.message}); } else { return Promise.resolve({type: "accept"}); } }; // Initialize Checkout const checkout = await stripe.initEmbeddedCheckout({ fetchClientSecret, onShippingDetailsChange, }); // Mount Checkout checkout.mount('#checkout'); } ``` #### React 以下の npm から [react-stripe-js](https://docs.stripe.com/sdks/stripejs-react.md) と Stripe.js ローダーをインストールします。 ```bash npm install --save @stripe/react-stripe-js @stripe/stripe-js ``` 公開可能な API キーを使用して `stripe` インスタンスを初期化します。 ```jsx import {loadStripe} from '@stripe/stripe-js'; // Make sure to call `loadStripe` outside of a component’s render to avoid // recreating the `Stripe` object on every render. const stripePromise = loadStripe('<>'); ``` 埋め込み型の Checkout コンポーネントを使用するには、`EmbeddedCheckoutProvider` を作成します。 サーバーに [Checkout セッション](https://docs.stripe.com/api/checkout/sessions/object.md)の作成をリクエストする非同期の `fetchClientSecret` 関数を作成し、クライアントシークレットを取得します。 顧客の配送先住所に基づいて配送オプションを計算するようにサーバーへリクエストする、非同期の `onShippingDetailsChange` 関数を作成します。顧客が配送先情報フォームの入力を終えると、Stripe Checkout がこの関数を呼び出します。 `stripePromise` をプロバイダーに渡し、プロバイダーが受け入れた `options` プロパティに関数を渡します。 ```jsx import * as React from 'react'; import { EmbeddedCheckoutProvider, EmbeddedCheckout } from '@stripe/react-stripe-js'; const App = () => { const fetchClientSecret = useCallback(() => { // Create a Checkout Session return fetch("/create-checkout-session", { method: "POST", }) .then((res) => res.json()) .then((data) => data.clientSecret); }, []); // Call your backend to set shipping options const onShippingDetailsChange = async (shippingDetailsChangeEvent) => { const {checkoutSessionId, shippingDetails} = shippingDetailsChangeEvent; const response = await fetch("/calculate-shipping-options", { method: "POST", body: JSON.stringify({ checkout_session_id: checkoutSessionId, shipping_details: shippingDetails, }) }) if (response.type === 'error') { return Promise.resolve({type: "reject", errorMessage: response.message}); } else { return Promise.resolve({type: "accept"}); } }; const options = {fetchClientSecret, onShippingDetailsChange}; return (
) } ``` > `onShippingDetailsChange` 関数からは必ず `Promise` が返され、[ResultAction](https://docs.stripe.com/js/embedded_checkout/init#embedded_checkout_init-options-onShippingDetailsChange-ResultAction) オブジェクトで解決されます。 Checkout クライアントは、`onShippingDetailsChange` 関数の結果に基づいて UI を更新します。 - 結果に `type: "accept"` が含まれる場合、Checkout UI はサーバーで設定した配送オプションを表示します。 - 結果に `type: "reject"` が含まれる場合、Checkout UI は結果で設定したエラーメッセージを表示します。 必要に応じて、`onShippingDetailsChange` をリッスンし、複数の可能な住所から希望の住所を選択して確認するための、カスタム UI を作成できます。 Checkout は、HTTPS 接続を介して支払い情報をStripeに安全に送信する iframe でレンダリングされます。 > 一部の支払い方法では、別のページにリダイレクトして支払いを確定する必要があるため、Checkout は別の iframe 内に配置しないでください。 ## 実装をテストする 実装内容をテストして、カスタムの配送オプションが正しく機能することを確認するには、以下のステップに従います。 1. 本番環境を反映したサンドボックス環境を設定します。この環境では、Stripe サンドボックスの API キーを使用してください。 1. さまざまな配送先住所をシミュレーションして、`calculateShippingOptions` 関数がシナリオを正しく処理していることを確認します。 1. ログツールやデバッグツールを使用してサーバーが以下を行っていることを確認し、サーバー側のロジックを検証します。 - [Checkout Session (セッション)](https://docs.stripe.com/api/checkout/sessions/object.md) を取得します。 - 配送の詳細を検証します。 - 配送オプションを計算します。 - 新しい配送の詳細とオプションで [Checkout Session (セッション)](https://docs.stripe.com/api/checkout/sessions/object.md) を更新します。更新のレスポンスに新しい配送の詳細とオプションが含まれていることを確認します。 1. ブラウザーで決済プロセスを複数回実行することで、クライアント側のロジックを検証します。配送の詳細の入力後に UI がどのように更新されるかに注意してください。次のことを確認してください。 - `onShippingDetailsChange` 関数が指定したとおりに呼び出されている。 - 配送オプションが指定された住所に基づいて正しく更新される。 - 配送先が利用できない場合にエラーメッセージが適切に表示される。 1. 無効な配送先住所を入力するか、サーバーエラーをシミュレーションして、サーバー側とクライアント側の両方のエラー処理をテストします。