# Stripe Checkout を使って、事前構築されたサブスクリプションページを作成する # Stripe Checkout を使用した構築済みのサブスクリプションページ サンプルアプリから始めて、[Stripe Billing](https://docs.stripe.com/billing.md) と [Stripe Checkout](https://docs.stripe.com/payments/checkout.md) を使用して、完全に機能するサブスクリプションシステムを実行します。 サンプルアプリでは、顧客のサイトを Stripe がホストする事前構築済みの支払いページにリダイレクトする方法を説明しています。Stripe Billing API は、定期購読、請求書、継続支払いの作成と管理を行い、Checkout は、事前構築済みの安全な Stripe ホストの UI を提供して支払いの詳細を収集します。 You can model customers in your integration either as [customer-configured Account](https://docs.stripe.com/api/v2/core/accounts/create.md#v2_create_accounts-configuration-customer) objects using the Accounts v2 API (recommended in most cases) or as [Customer](https://docs.stripe.com/api/customers/object.md) objects using the Customers v1 API. For details about the differences between these options, see [Use Accounts as customers](https://docs.stripe.com/accounts-v2/use-accounts-as-customers.md). 各ステップをクリックすると、対応するサンプルコードが表示されます。料金データの追加などのステップを操作すると、ビルダーがサンプルコードを更新します。 サンプルアプリをローカルにダウンロードしてカスタマイズし、実装をテストします。 ### 商品と料金を追加する このサンプルで使用できる新しい*商品* (Products represent what your business sells—whether that's a good or a service)と*価格* (Prices define how much and how often to charge for products. This includes how much the product costs, what currency to use, and the interval if the price is for subscriptions)を作成します。 > 商品と価格を設定するには、Stripe アカウントにサインインしてください。 ### 商品に機能を追加する 誕生日ギフトなどの機能を作成し、それをサブスクリプションに関連付け、新しい登録者に[エンタイトルメント](https://docs.stripe.com/billing/entitlements.md)を付与します。[イベント送信先](https://docs.stripe.com/event-destinations.md)の[有効なエンタイトルメントのサマリーイベント](https://docs.stripe.com/billing/entitlements.md#webhooks)をリッスンし、指定された顧客に [List active entitlements API](https://docs.stripe.com/api/entitlements/active-entitlement/list.md) を使用して顧客のエンタイトルメントを履行します。 ### (任意) 決済手段を有効にする [ダッシュボード](https://dashboard.stripe.com/settings/payment_methods)を使用して、カード以外の受け付け可能な[決済手段](https://docs.stripe.com/payments/payment-methods/payment-method-support.md)を有効にします。Checkout では、顧客の所在地やその他の情報に基づいて、有効な決済手段が関連度の高い順に表示されます。 ### 料金のプレビューページを追加する 商品を表示するページをサイトに追加し、顧客がその商品をサブスクリプション登録できるようにします。**購入** をクリックすると、顧客は Stripe 上のオンライン [Checkout](https://docs.stripe.com/payments/checkout.md) ページにリダイレクトされます。そこで注文が確定され、それ以上は変更されません。 [料金表](https://docs.stripe.com/payments/checkout/pricing-table.md)を組み込み、ダッシュボードで料金情報を動的に表示することを検討してください。料金オプションをクリックすると、顧客は決済ページにリダイレクトされます。 ### 購入ボタンを追加する 注文プレビューページのボタンは、顧客を Stripe がオンラインで提供する決済ページにリダイレクトし、商品の`lookup_key`を使用してサーバーから`price_id`を取得します。 ### 成功ページを追加する 成功ページを作成して、注文確認メッセージや注文詳細を顧客に表示します。このページを Checkout セッション `success_url` に関連付けます。顧客が決済を正常に完了すると、このページにリダイレクトされます。 ### カスタマーポータルボタンを追加する 顧客がサブスクリプションを各自で管理できるように、カスタマーポータルにリダイレクトするボタンを追加します。このボタンをクリックすると、顧客は Stripe がオンラインで提供するカスタマーポータルページにリダイレクトされます。 ### カスタマーポータルセッションにリダイレクトする サーバー上のエンドポイントに対してリクエストを行い、新しいカスタマーポータルセッションにリダイレクトします。この例は、[Checkout Session (セッション)](https://docs.stripe.com/api/checkout/sessions/object.md#checkout_session_object-id) の `session_id` を使用して `customer_id` を取得する方法を示しています。本番環境では、この値を認証済みユーザーと一緒にデータベースに保存することをお勧めします。 ### Stripe Node ライブラリーをインストールする パッケージをインストールし、それをコードにインポートします。また、まったくゼロから開始していて package.json ファイルが必要な場合には、コードエディターのダウンロードリンクを使用してプロジェクトファイルをダウンロードします。 #### npm ライブラリーをインストールします。 ```bash npm install --save stripe ``` #### GitHub または、stripe-node ライブラリーのソースコードを直接 [GitHub から](https://github.com/stripe/stripe-node)ダウンロードします。 ### Stripe Ruby ライブラリーをインストールする Stripe ruby gem をインストールし、require を指定してコードに読み込みます。または、まったくゼロから開始していて Gemfile が必要な場合には、コードエディターのリンクを使用してプロジェクトファイルをダウンロードします。 #### Terminal gem をインストールします。 ```bash gem install stripe ``` #### Bundler この行を Gemfile に追加します。 ```bash gem 'stripe' ``` #### GitHub または、stripe-ruby gem のソースコードを直接 [GitHub から](https://github.com/stripe/stripe-ruby)ダウンロードします。 ### Stripe Java ライブラリーをインストールする ビルドに依存関係を追加し、ライブラリーをインポートします。まったくゼロから開始していてサンプルの pom.xml ファイル (Maven 用) が必要な場合は、コードエディターのリンクを使用してプロジェクトファイルをダウンロードします。 #### Maven POM に以下の依存関係を追加し、{VERSION} を使用するバージョン番号に置き換えます。 ```bash \ncom.stripe\nstripe-java\n{VERSION}\n ``` #### Gradle build.gradle ファイルに依存関係を追加し、{VERSION} を使用するバージョン番号に置き換えます。 ```bash implementation "com.stripe:stripe-java:{VERSION}" ``` #### GitHub JAR を直接 [GitHub から](https://github.com/stripe/stripe-java/releases/latest)ダウンロードします。 ### Stripe Python パッケージをインストールする Stripe パッケージをインストールし、コードにインポートします。まったくゼロから開始していて requirements.txt が必要な場合には、コードエディターのリンクを使用してプロジェクトファイルをダウンロードします。 #### pip pip を使用してパッケージをインストールします。 ```bash pip3 install stripe ``` #### GitHub stripe-python ライブラリのソースコードを [GitHub から](https://github.com/stripe/stripe-python)直接ダウンロードします。 ### Stripe PHP ライブラリーをインストールする Composer を使用してライブラリーをインストールし、シークレット API キーで初期化します。まったくゼロから開始していて composer.json ファイルが必要な場合には、コードエディターのリンクを使用してファイルをダウンロードします。 #### Composer ライブラリーをインストールします。 ```bash composer require stripe/stripe-php ``` #### GitHub または、stripe-php ライブラリーのソースコードを直接 [GitHub から](https://github.com/stripe/stripe-php)ダウンロードします。 ### サーバーを設定する ビルドに依存関係を追加し、ライブラリーをインポートします。まったくゼロから開始していて go.mod ファイルが必要な場合には、コードエディターのリンクを使用してプロジェクトファイルをダウンロードします。 #### Go 必ず Go モジュールを使用してを初期化してください。 ```bash go get -u github.com/stripe/stripe-go/v85 ``` #### GitHub または、stripe-go モジュールのソースコードを直接 [GitHub から](https://github.com/stripe/stripe-go)ダウンロードします。 ### Stripe.net ライブラリーをインストールする .NET または NuGet でパッケージをインストールします。まったくゼロから開始する場合には、設定済みの .csproj ファイルが含まれるファイルをダウンロードします。 #### dotnet ライブラリーをインストールします。 ```bash dotnet add package Stripe.net ``` #### NuGet ライブラリーをインストールします。 ```bash Install-Package Stripe.net ``` #### GitHub または、Stripe.net ライブラリーのソースコードを直接 [GitHub から](https://github.com/stripe/stripe-dotnet)ダウンロードします。 ### Stripe ライブラリーをインストールする パッケージをインストールし、コードにインポートします。まったくゼロから開始していて `package.json` が必要な場合には、コードエディターのリンクを使用してプロジェクトファイルをダウンロードします。 ライブラリーをインストールします。 ```bash npm install --save stripe @stripe/stripe-js next ``` ### 決済フローセッションを作成する [Checkout セッション](https://docs.stripe.com/api/checkout/sessions.md)は、項目、注文金額と通貨、および受け付け可能な決済手段など、Stripe 上のオンライン決済ページで顧客に表示する内容を制御します。 ### 検索キーから価格を取得する [Price](https://docs.stripe.com/api/prices/list.md) エンドポイントで商品に定義した検索キーを渡し、その料金を注文に適用します。 ### ラインアイテムを定義する 価格や在庫状況など、商品在庫に関する機密情報は常に自社のサーバーに置き、顧客がクライアントから操作できないようにします。上記で取得した事前定義された価格 ID を渡します。 ### モードを設定する モードを `subscription` に設定します。Checkout は、継続的ではない支払いに対して[支払い](https://docs.stripe.com/checkout/quickstart.md)モードと[設定](https://docs.stripe.com/payments/save-and-reuse.md)モードもサポートしています。 ### 成功 URL を指定 成功後に Stripe が顧客をリダイレクトできる、一般にアクセス可能な URL を指定します。URL の最後に `session_id` クエリパラメーターを追加して、後で顧客を取得できるようにし、Stripe が顧客のホスト型ダッシュボードを生成できるようにします。 ### Checkout からリダイレクトする セッションを作成したら、レスポンスで返された URL (成功またはキャンセル時の URL) に顧客をリダイレクトします。 ### カスタマーポータルセッションを作成する 安全な Stripe 上のオンライン[カスタマーポータルセッション](https://docs.stripe.com/api/customer_portal/sessions/create.md)を開始します。このセッションで、顧客はサブスクリプションと請求の詳細を管理できます。 ### カスタマーポータルにリダイレクトする ポータルセッションを作成したら、レスポンスで返された URL に顧客をリダイレクトします。 ### サブスクリプションのフルフィルメントを実行する `/webhook` エンドポイントを作成し、Workbench の [Webhook](https://dashboard.stripe.com/webhooks) タブで Webhook シークレットキーを取得して、サブスクリプションアクティビティーに関連するイベントをリッスンします。支払いが成功し、成功ページにリダイレクトされたら、サブスクリプションのステータスが `active` であることを確認し、登録した商品と機能に対するアクセス権を顧客に付与します。 ### サーバーを実行する サーバーを起動し、 に移動します ```bash npm start ``` ### サーバーを実行する サーバーを起動します。自動的にブラウザーウィンドウが開き、 が表示されます ```bash npm start ``` ### サーバーを実行する サーバーを起動し、 に移動します ```bash ruby server.rb ``` ### サーバーを実行する サーバーを起動します。自動的にブラウザーウィンドウが開き、 が表示されます ```bash ruby server.rb ``` ### サーバーを実行する サーバーを起動し、 に移動します ```bash python3 -m flask run --port=4242 ``` ### サーバーを実行する サーバーを起動します。自動的にブラウザーウィンドウが開き、 が表示されます ```bash python3 -m flask run --port=4242 ``` ### サーバーを実行する サーバーを起動し、 に移動します ```bash php -S 127.0.0.1:4242 --docroot=public ``` ### サーバーを実行する サーバーを起動します。自動的にブラウザーウィンドウが開き、 が表示されます ```bash php -S 127.0.0.1:4242 --docroot=public ``` ### サーバーを実行する サーバーを起動し、 に移動します ```bash dotnet run ``` ### サーバーを実行する サーバーを起動します。自動的にブラウザーウィンドウが開き、 が表示されます ```bash dotnet run ``` ### サーバーを実行する サーバーを起動し、 に移動します ```bash go run server.go ``` ### サーバーを実行する サーバーを起動します。自動的にブラウザーウィンドウが開き、 が表示されます ```bash go run server.go ``` ### サーバーを実行する サーバーを起動し、 に移動します ```bash java -cp target/sample-jar-with-dependencies.jar com.stripe.sample.Server ``` ### サーバーを実行する サーバーを起動します。自動的にブラウザーウィンドウが開き、 が表示されます ```bash java -cp target/sample-jar-with-dependencies.jar com.stripe.sample.Server ``` ### 試してみる 決済ボタンをクリックします。Stripe がオンラインで提供する決済ページで、いずれかのテストカードを使用して決済をシミュレーションします。 | Scenario | Card Number | | ----------------------------------- | ---------------- | | Payment succeeds | 4242424242424242 | | Payment requires 3DS authentication | 4000002500003155 | | Payment is declined | 4000000000009995 | ## カスタマイズ機能を追加する テストで商品のサブスクリプションに登録すると、基本的なサブスクリプションの決済フロー組み込みが機能します。以下のトグルを使用して、このサンプルを追加機能でカスタマイズする方法を確認してください。 ### トライアルを追加する トライアル期間を決済フローセッションに関連付けます。 ### トライアル期間を追加する `subscription_data` を使用して、顧客に初めて請求する前に `trial_period_days` の回数を表す整数を追加します。`1` 以上を指定する必要があります。 決済手段を指定せずに無料トライアルを開始する場合は、`trial_settings[end_behavior][missing_payment_method]` フィールドを `pause` または `cancel` に設定して、決済手段を指定せずにトライアルが終了した場合にサブスクリプションが継続されないようにします。Checkout Session を作成するときにこのパラメーターを `subscription_data` に渡すか、別のタイミングでサブスクリプションを更新します。詳細については、[トライアル期間を使用する](https://docs.stripe.com/billing/subscriptions/trials/free-trials.md#create-free-trials-without-payment)をご覧ください。 ### 請求サイクル日を設定する Checkout セッションを作成するときに請求サイクルの起点を指定します。 ### サブスクリプションの請求サイクルの起点 `subscription_data` を使用して、サブスクリプションの次回の請求日の `billing_cycle_anchor` タイムスタンプを設定します。詳細については、[Checkout で請求サイクル日を設定する](https://docs.stripe.com/payments/checkout/billing-cycle.md)方法をご覧ください。 ### 税金の徴収を自動化する Stripe 取引の適切な税額を計算して徴収します。[Stripe Tax](https://docs.stripe.com/tax.md) と、[Checkout への追加方法](https://docs.stripe.com/tax/checkout.md)の詳細をご確認ください。導入前に、ダッシュボードで [Stripe Tax を有効にします](https://dashboard.stripe.com/tax)。 ### 税金の自動計算パラメーターを追加する `automatic_tax` パラメーターを `enabled: true` に設定します。

{{PRODUCT_NAME}}

{{FORMATTED_RECURRING_PRICE}}

Subscription to Starter plan successful!

Picked the wrong subscription? Shop around then come back to pay!

{{PRODUCT_NAME}}

{{FORMATTED_RECURRING_PRICE}}
{/* Add a hidden field with the lookup_key of your Price */}
{/* Add a hidden field with the lookup_key of your Price */}

Subscription to {{PRODUCT_NAME}} successful!

{ "name": "stripe-sample", "version": "0.1.0", "dependencies": { "@stripe/react-stripe-js": "^3.7.0", "@stripe/stripe-js": "^7.3.0", "express": "^4.17.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "^5.0.1", "stripe": "^8.202.0" }, "devDependencies": { "concurrently": "4.1.2" }, "homepage": "http://localhost:3000/checkout", "proxy": "http://127.0.0.1:4242", "scripts": { "start-client": "react-scripts start", "start-server": "node server.js", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject", "start": "concurrently \"yarn start-client\" \"yarn start-server\"" }, "eslintConfig": { "extends": "react-app" }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] } } { "name": "client", "version": "0.1.0", "private": true, "dependencies": { "@stripe/react-stripe-js": "^3.7.0", "@stripe/stripe-js": "^7.3.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "^5.0.1" }, "homepage": "http://localhost:3000/checkout", "proxy": "http://127.0.0.1:4242", "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" }, "eslintConfig": { "extends": "react-app" }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] } } // This is a public sample test API key. // Don’t submit any personally identifiable information in requests made with this key. // Sign in to see your own test API key embedded in code samples. // Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. const stripe = require('stripe')('<>'); const YOUR_DOMAIN = "http://localhost:4242"; const YOUR_DOMAIN = "http://localhost:3000"; const prices = await stripe.prices.list({ lookup_keys: [req.body.lookup_key], expand: ['data.product'], }); const session = await stripe.checkout.sessions.create({ billing_address_collection: 'auto', line_items: [ { price: prices.data[0].id, // For usage-based billing, don't pass quantity quantity: 1, }, ], mode: 'subscription', success_url: `${YOUR_DOMAIN}/success.html?session_id={CHECKOUT_SESSION_ID}`, success_url: `${YOUR_DOMAIN}/?success=true&session_id={CHECKOUT_SESSION_ID}`, discounts: [{ coupon: '{{COUPON_ID}}', }], customer: 'cus_123', customer_account: 'acct_123', subscription_data: { trial_period_days: 7, billing_cycle_anchor: 1672531200, }, subscription_data: { billing_cycle_anchor: 1672531200, }, automatic_tax: { enabled: true }, }); res.redirect(303, session.url); // This is the url to which the customer will be redirected when they're done // managing their billing with the portal. const returnUrl = YOUR_DOMAIN; const portalSession = await stripe.billingPortal.sessions.create({ customer: checkoutSession.customer, return_url: returnUrl, }); // This is the url to which the customer will be redirected when they're done // managing their billing with the portal. const returnUrl = YOUR_DOMAIN; const portalSession = await stripe.billingPortal.sessions.create({ customer_account: checkoutSession.customer_account, return_url: returnUrl, }); res.redirect(303, portalSession.url); app.post( '/webhook', express.raw({ type: 'application/json' }), (request, response) => { let event = request.body; // Replace this endpoint secret with your endpoint's unique secret // If you are testing with the CLI, find the secret by running 'stripe listen' // If you are using an endpoint defined with the API or dashboard, look in your webhook settings // at https://dashboard.stripe.com/webhooks const endpointSecret = 'whsec_12345'; // Only verify the event if you have an endpoint secret defined. // Otherwise use the basic event deserialized with JSON.parse if (endpointSecret) { // Get the signature sent by Stripe const signature = request.headers['stripe-signature']; try { event = stripe.webhooks.constructEvent( request.body, signature, endpointSecret ); } catch (err) { console.log(`⚠️ Webhook signature verification failed.`, err.message); return response.sendStatus(400); } } let subscription; let status; // Handle the event switch (event.type) { case 'customer.subscription.trial_will_end': subscription = event.data.object; status = subscription.status; console.log(`Subscription status is ${status}.`); // Then define and call a method to handle the subscription trial ending. // handleSubscriptionTrialEnding(subscription); break; case 'customer.subscription.deleted': subscription = event.data.object; status = subscription.status; console.log(`Subscription status is ${status}.`); // Then define and call a method to handle the subscription deleted. // handleSubscriptionDeleted(subscriptionDeleted); break; case 'customer.subscription.created': subscription = event.data.object; status = subscription.status; console.log(`Subscription status is ${status}.`); // Then define and call a method to handle the subscription created. // handleSubscriptionCreated(subscription); break; case 'customer.subscription.updated': subscription = event.data.object; status = subscription.status; console.log(`Subscription status is ${status}.`); // Then define and call a method to handle the subscription update. // handleSubscriptionUpdated(subscription); break; case 'entitlements.active_entitlement_summary.updated': subscription = event.data.object; console.log(`Active entitlement summary updated for ${subscription}.`); // Then define and call a method to handle active entitlement summary updated // handleEntitlementUpdated(subscription); break; default: // Unexpected event type console.log(`Unhandled event type ${event.type}.`); } // Return a 200 response to acknowledge receipt of the event response.send(); } ); { "name": "stripe-sample", "version": "1.0.0", "description": "A sample Stripe implementation", "main": "server.js", "scripts": { "start": "node server.js" }, "author": "stripe-samples", "license": "ISC", "dependencies": { "express": "^4.17.1", "stripe": "^21.0.1" } } { "name": "stripe-sample", "version": "0.1.0", "dependencies": { "@stripe/react-stripe-js": "^3.7.0", "@stripe/stripe-js": "^7.3.0", "express": "^4.17.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "^5.0.1", "stripe": "21.0.1" }, "devDependencies": { "concurrently": "4.1.2" }, "homepage": "http://localhost:3000/checkout", "proxy": "http://localhost:4242", "scripts": { "start-client": "react-scripts start", "start-server": "node server.js", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject", "start": "concurrently \"yarn start-client\" \"yarn start-server\"" }, "eslintConfig": { "extends": "react-app" }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] } } \# This is a public sample test API key. # Don’t submit any personally identifiable information in requests made with this key. # Sign in to see your own test API key embedded in code samples. \# Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. client = Stripe::StripeClient.new('<>') prices = client.v1.prices.list( lookup_keys: [params['lookup_key']], expand: ['data.product'] ) session = client.v1.checkout.sessions.create({ mode: 'subscription', line_items: [{ quantity: 1, price: prices.data[0].id }], success_url: YOUR_DOMAIN + '/success.html?session_id={CHECKOUT_SESSION_ID}', success_url: YOUR_DOMAIN + '?success=true&session_id={CHECKOUT_SESSION_ID}', subscription_data: { trial_period_days: 7, billing_cycle_anchor: 1672531200 }, subscription_data: { billing_cycle_anchor: 1672531200 }, customer: 'cus_JyTTNqVDAoRYE1', customer_account: 'acct_123', discounts: [{ coupon: 'gBY6sFUf' }], automatic_tax: { enabled: true }, }) redirect session.url, 303 session = client.v1.billing_portal.sessions.create({ customer: checkout_session.customer, return_url: return_url }) session = client.v1.billing_portal.sessions.create({ customer_account: checkout_session.customer_account, return_url: return_url }) redirect session.url, 303 post '/webhook' do \# Replace this endpoint secret with your endpoint's unique secret # If you are testing with the CLI, find the secret by running 'stripe listen' # If you are using an endpoint defined with the API or dashboard, look in your webhook settings # at https://dashboard.stripe.com/webhooks webhook_secret = 'whsec_12345' payload = request.body.read if !webhook_secret.empty? # Retrieve the event by verifying the signature using the raw body and secret if webhook signing is configured. sig_header = request.env['HTTP_STRIPE_SIGNATURE'] event = nil begin event = Stripe::Webhook.construct_event( payload, sig_header, webhook_secret ) rescue JSON::ParserError => e # Invalid payload status 400 return rescue Stripe::SignatureVerificationError => e # Invalid signature puts '⚠️ Webhook signature verification failed.' status 400 return end else data = JSON.parse(payload, symbolize_names: true) event = Stripe::Event.construct_from(data) end # Get the type of webhook event sent - used to check the status of PaymentIntents. event_type = event['type'] data = event['data'] data_object = data['object'] if event.type == 'customer.subscription.deleted' # handle subscription canceled automatically based # upon your subscription settings. Or if the user cancels it. # puts data_object puts "Subscription canceled: #{event.id}" end if event.type == 'customer.subscription.updated' # handle subscription updated # puts data_object puts "Subscription updated: #{event.id}" end if event.type == 'customer.subscription.created' # handle subscription created # puts data_object puts "Subscription created: #{event.id}" end if event.type == 'customer.subscription.trial_will_end' # handle subscription trial ending # puts data_object puts "Subscription trial will end: #{event.id}" end if event.type == 'entitlements.active_entitlement_summary.updated' # handle active entitlement summary updated # puts data_object puts "Active entitlement summary updated: #{event.id}" end content_type 'application/json' { status: 'success' }.to_json end import stripe \# This is a public sample test API key. # Don’t submit any personally identifiable information in requests made with this key. # Sign in to see your own test API key embedded in code samples. \# Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. client = stripe.StripeClient('<>') try: prices = client.v1.prices.list(params={ 'lookup_keys': [request.form['lookup_key']], 'expand': ['data.product'], }) checkout_session = client.v1.checkout.sessions.create(params={ 'line_items': [ { 'price': prices.data[0].id, 'quantity': 1, }, ], 'mode': 'subscription', 'success_url': YOUR_DOMAIN + '/success.html?session_id={CHECKOUT_SESSION_ID}', 'success_url': YOUR_DOMAIN + '?success=true&session_id={CHECKOUT_SESSION_ID}', 'subscription_data': { 'trial_period_days': 7, 'billing_cycle_anchor': 1672531200, }, 'subscription_data': { 'billing_cycle_anchor': 1672531200, }, 'discounts': [ { 'coupon': '{{COUPON_ID}}' } ], 'customer': 'cus_123', 'customer_account': 'acct_123', 'automatic_tax': { 'enabled': True }, }) return redirect(checkout_session.url, code=303) portalSession = client.v1.billing_portal.sessions.create(params={ 'customer': checkout_session.customer, 'return_url': return_url, }) portalSession = stripe.billing_portal.Session.create( customer_account=checkout_session.customer_account, return_url=return_url, ) return redirect(portalSession.url, code=303) @app.route('/webhook', methods=['POST']) def webhook_received(): \# Replace this endpoint secret with your endpoint's unique secret # If you are testing with the CLI, find the secret by running 'stripe listen' # If you are using an endpoint defined with the API or dashboard, look in your webhook settings # at https://dashboard.stripe.com/webhooks webhook_secret = 'whsec_12345' request_data = json.loads(request.data) if webhook_secret: # Retrieve the event by verifying the signature using the raw body and secret if webhook signing is configured. signature = request.headers.get('stripe-signature') try: event = client.construct_event( payload=request.data, sig_header=signature, secret=webhook_secret) data = event['data'] except Exception as e: return e # Get the type of webhook event sent - used to check the status of PaymentIntents. event_type = event['type'] else: data = request_data['data'] event_type = request_data['type'] data_object = data['object'] print('event ' + event_type) if event_type == 'checkout.session.completed': print('🔔 Payment succeeded!') elif event_type == 'customer.subscription.trial_will_end': print('Subscription trial will end') elif event_type == 'customer.subscription.created': print('Subscription created %s', event.id) elif event_type == 'customer.subscription.updated': print('Subscription created %s', event.id) elif event_type == 'customer.subscription.deleted': # handle subscription canceled automatically based # upon your subscription settings. Or if the user cancels it. print('Subscription canceled: %s', event.id) elif event_type == 'entitlements.active_entitlement_summary.updated': # handle active entitlement summary updated print('Active entitlement summary updated: %s', event.id) return jsonify({'status': 'success'}) certifi==2026.1.4 chardet==5.2.0 click==8.3.1 Flask==3.1.2 idna==3.11 itsdangerous==2.2.0 Jinja2==3.1.6 MarkupSafe==3.0.3 requests==2.32.5 stripe==15.0.0 toml==0.10.2 Werkzeug==3.1.5 $session = $stripe->billingPortal->sessions->create([ 'customer' => $checkout_session->customer, 'return_url' => $return_url, ]); $session = $stripe->billingPortal->sessions->create([ 'customer_account' => $checkout_session->customer_account, 'return_url' => $return_url, ]); header("HTTP/1.1 303 See Other"); header("Location: " . $session->url); // Replace this endpoint secret with your endpoint's unique secret // If you are testing with the CLI, find the secret by running 'stripe listen' // If you are using an endpoint defined with the API or dashboard, look in your webhook settings // at https://dashboard.stripe.com/webhooks $endpoint_secret = 'whsec_12345'; $payload = @file_get_contents('php://input'); $event = null; try { $event = \Stripe\Event::constructFrom( json_decode($payload, true) ); } catch(\UnexpectedValueException $e) { // Invalid payload echo '⚠️ Webhook error while parsing basic request.'; http_response_code(400); exit(); } // Handle the event switch ($event->type) { case 'customer.subscription.trial_will_end': $subscription = $event->data->object; // contains a \Stripe\Subscription // Then define and call a method to handle the trial ending. // handleTrialWillEnd($subscription); break; case 'customer.subscription.created': $subscription = $event->data->object; // contains a \Stripe\Subscription // Then define and call a method to handle the subscription being created. // handleSubscriptionCreated($subscription); break; case 'customer.subscription.deleted': $subscription = $event->data->object; // contains a \Stripe\Subscription // Then define and call a method to handle the subscription being deleted. // handleSubscriptionDeleted($subscription); break; case 'customer.subscription.updated': $subscription = $event->data->object; // contains a \Stripe\Subscription // Then define and call a method to handle the subscription being updated. // handleSubscriptionUpdated($subscription); break; case 'entitlements.active_entitlement_summary.updated': $subscription = $event->data->object; // contains a \Stripe\Subscription // Then define and call a method to handle active entitlement summary updated. // handleEntitlementUpdated($subscription); break; default: // Unexpected event type echo 'Received unknown event type'; } // Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. $stripeSecretKey = '<>'; $stripe = new \Stripe\StripeClient($stripeSecretKey); $prices = $stripe->prices->all([ // retrieve lookup_key from form data POST body 'lookup_keys' => [$_POST['lookup_key']], 'expand' => ['data.product'] ]); $checkout_session = $stripe->checkout->sessions->create([ 'line_items' => [[ 'price' => $prices->data[0]->id, 'quantity' => 1, ]], 'mode' => 'subscription', 'success_url' => $YOUR_DOMAIN . '/success.html?session_id={CHECKOUT_SESSION_ID}', 'success_url' => $YOUR_DOMAIN . '?success=true&session_id={CHECKOUT_SESSION_ID}', 'subscription_data' => [ 'trial_period_days' => 7, 'billing_cycle_anchor' => 1672531200, ], 'subscription_data' => [ 'billing_cycle_anchor' => 1672531200, ], 'discounts' => [[ 'coupon' => '{{COUPON_ID}}', ]], 'customer' => 'cus_123', 'automatic_tax' => [ 'enabled' => true, ], ]); header("HTTP/1.1 303 See Other"); header("Location: " . $checkout_session->url); // This is a public sample test API key. // Don’t submit any personally identifiable information in requests made with this key. // Sign in to see your own test API key embedded in code samples. // Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. services.AddSingleton(new StripeClient("<>")); var priceOptions = new PriceListOptions { LookupKeys = new List { Request.Form["lookup_key"] } }; StripeList prices = _client.V1.Prices.List(priceOptions); var options = new SessionCreateOptions { LineItems = new List { new SessionLineItemOptions { Price = prices.Data[0].Id, Quantity = 1, }, }, Mode = "subscription", SuccessUrl = domain + "/success.html?session_id={CHECKOUT_SESSION_ID}", SuccessUrl = domain + "?success=true&session_id={CHECKOUT_SESSION_ID}", SubscriptionData = new SessionSubscriptionDataOptions { TrialPeriodDays = 7, BillingCycleAnchor = 1672531200, }, SubscriptionData = new SessionSubscriptionDataOptions { BillingCycleAnchor = 1672531200, }, Customer = "cus_123", CustomerAccount: stripe.String("acct_123"), AutomaticTax = new SessionAutomaticTaxOptions { Enabled = true }, CustomerAccount = "acct_123", }; Session session = _client.V1.Checkout.Sessions.Create(options); Response.Headers.Add("Location", session.Url); return new StatusCodeResult(303); var options = new Stripe.BillingPortal.SessionCreateOptions { Customer = checkoutSession.CustomerId, ReturnUrl = returnUrl, }; var session = _client.V1.BillingPortal.Sessions.Create(options); var options = new Stripe.BillingPortal.SessionCreateOptions { CustomerAccount = checkoutSession.CustomerAccount, ReturnUrl = returnUrl, }; var session = _client.V1.BillingPortal.Sessions.Create(options); Response.Headers.Add("Location", session.Url); return new StatusCodeResult(303); [Route("webhook")] [ApiController] public class WebhookController : Controller { [HttpPost] public async Task Index() { var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync(); // Replace this endpoint secret with your endpoint's unique secret // If you are testing with the CLI, find the secret by running 'stripe listen' // If you are using an endpoint defined with the API or dashboard, look in your webhook settings // at https://dashboard.stripe.com/webhooks const string endpointSecret = "whsec_12345"; try { var stripeEvent = EventUtility.ParseEvent(json); var signatureHeader = Request.Headers["Stripe-Signature"]; stripeEvent = EventUtility.ConstructEvent(json, signatureHeader, endpointSecret); // If on SDK version < 46, use class Events instead of EventTypes if (stripeEvent.Type == EventTypes.CustomerSubscriptionDeleted) { var subscription = stripeEvent.Data.Object as Subscription; Console.WriteLine("A subscription was canceled.", subscription.Id); // Then define and call a method to handle the successful payment intent. // handleSubscriptionCanceled(subscription); } else if (stripeEvent.Type == EventTypes.CustomerSubscriptionUpdated) { var subscription = stripeEvent.Data.Object as Subscription; Console.WriteLine("A subscription was updated.", subscription.Id); // Then define and call a method to handle the successful payment intent. // handleSubscriptionUpdated(subscription); } else if (stripeEvent.Type == EventTypes.CustomerSubscriptionCreated) { var subscription = stripeEvent.Data.Object as Subscription; Console.WriteLine("A subscription was created.", subscription.Id); // Then define and call a method to handle the successful payment intent. // handleSubscriptionUpdated(subscription); } else if (stripeEvent.Type == EventTypes.CustomerSubscriptionTrialWillEnd) { var subscription = stripeEvent.Data.Object as Subscription; Console.WriteLine("A subscription trial will end", subscription.Id); // Then define and call a method to handle the successful payment intent. // handleSubscriptionUpdated(subscription); } else if (stripeEvent.Type == EventTypes.ActiveEntitlementSummaryUpdated) { var summary = stripeEvent.Data.Object as ActiveEntitlementSummary; Console.WriteLine("Active entitlement summary updated for customer", summary.Customer); // Then define and call a method to handle active entitlement summary updated. // handleEntitlementUpdated($subscription); } else { Console.WriteLine("Unhandled event type: {0}", stripeEvent.Type); } return Ok(); } catch (StripeException e) { Console.WriteLine("Error: {0}", e.Message); return BadRequest(); } } "github.com/stripe/stripe-go/v85" "github.com/stripe/stripe-go/v85/webhook" // This is a public sample test API key. // Don’t submit any personally identifiable information in requests made with this key. // Sign in to see your own test API key embedded in code samples. // Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. sc = stripe.NewClient("<>") params := &stripe.PriceListParams{ LookupKeys: stripe.StringSlice([]string{ lookup_key, }), } var price *stripe.Price for p, err := range sc.V1Prices.List(context.TODO(), params).All(context.TODO()) { if err != nil { log.Printf("sc.V1Prices.List: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } price = p break } if price == nil { log.Printf(">>>>>>>>>>>>>>>>>>>>>>>>>>> Add a price lookup key to checkout.html for the demo <<<<<<<<<<<<<<<<<<<<<<<<") return } checkoutParams := &stripe.CheckoutSessionCreateParams{ Mode: stripe.String(stripe.CheckoutSessionModeSubscription), LineItems: []*stripe.CheckoutSessionCreateLineItemParams{ &stripe.CheckoutSessionCreateLineItemParams{ Price: stripe.String(price.ID), Quantity: stripe.Int64(1), }, }, SubscriptionData: &stripe.CheckoutSessionCreateSubscriptionDataParams{ TrialPeriodDays: stripe.Int64(7), BillingCycleAnchor: stripe.Int64(1672531200), }, SubscriptionData: &stripe.CheckoutSessionCreateSubscriptionDataParams{ BillingCycleAnchor: stripe.Int64(1672531200), }, Discounts: []*stripe.CheckoutSessionCreateDiscountParams{ &stripe.CheckoutSessionCreateDiscountParams{ Coupon: stripe.String("gBY6sFUf"), }, }, SuccessURL: stripe.String(domain + "/success.html?session_id={CHECKOUT_SESSION_ID}"), SuccessURL: stripe.String(domain + "?success=true&session_id={CHECKOUT_SESSION_ID}"), Customer: stripe.String("cus_123"), CustomerAccount: stripe.String("acct_123"), AutomaticTax: &stripe.CheckoutSessionCreateAutomaticTaxParams{ Enabled: stripe.Bool(true), }, } s, err := sc.V1CheckoutSessions.Create(context.TODO(), checkoutParams) http.Redirect(w, r, s.URL, http.StatusSeeOther) // Authenticate your user. params := &stripe.BillingPortalSessionCreateParams{ Customer: stripe.String(s.Customer.ID), ReturnURL: stripe.String(domain), } ps, _ := sc.V1BillingPortalSessions.Create(context.TODO(), params) log.Printf("sc.V1BillingPortalSessions.Create: %v", ps.URL) // Authenticate your user. params := &stripe.BillingPortalSessionCreateParams{ CustomerAccount: stripe.String(s.CustomerAccount), ReturnURL: stripe.String(domain), } ps, _ := sc.V1BillingPortalSessions.Create(context.TODO(), params) log.Printf("sc.V1BillingPortalSessions.Create: %v", ps.URL) http.Redirect(w, r, ps.URL, http.StatusSeeOther) func handleWebhook(w http.ResponseWriter, req *http.Request) { const MaxBodyBytes = int64(65536) bodyReader := http.MaxBytesReader(w, req.Body, MaxBodyBytes) payload, err := ioutil.ReadAll(bodyReader) if err != nil { fmt.Fprintf(os.Stderr, "Error reading request body: %v\n", err) w.WriteHeader(http.StatusServiceUnavailable) return } // Replace this endpoint secret with your endpoint's unique secret // If you are testing with the CLI, find the secret by running 'stripe listen' // If you are using an endpoint defined with the API or dashboard, look in your webhook settings // at https://dashboard.stripe.com/webhooks endpointSecret := "whsec_12345" signatureHeader := req.Header.Get("Stripe-Signature") event, err := sc.ConstructEvent(payload, signatureHeader, endpointSecret) if err != nil { fmt.Fprintf(os.Stderr, "⚠️ Webhook signature verification failed. %v\n", err) w.WriteHeader(http.StatusBadRequest) // Return a 400 error on a bad signature return } // Unmarshal the event data into an appropriate struct depending on its Type switch event.Type { case "customer.subscription.deleted": var subscription stripe.Subscription err := json.Unmarshal(event.Data.Raw, &subscription) if err != nil { fmt.Fprintf(os.Stderr, "Error parsing webhook JSON: %v\n", err) w.WriteHeader(http.StatusBadRequest) return } log.Printf("Subscription deleted for %d.", subscription.ID) // Then define and call a func to handle the deleted subscription. // handleSubscriptionCanceled(subscription) case "customer.subscription.updated": var subscription stripe.Subscription err := json.Unmarshal(event.Data.Raw, &subscription) if err != nil { fmt.Fprintf(os.Stderr, "Error parsing webhook JSON: %v\n", err) w.WriteHeader(http.StatusBadRequest) return } log.Printf("Subscription updated for %d.", subscription.ID) // Then define and call a func to handle the successful attachment of a PaymentMethod. // handleSubscriptionUpdated(subscription) case "customer.subscription.created": var subscription stripe.Subscription err := json.Unmarshal(event.Data.Raw, &subscription) if err != nil { fmt.Fprintf(os.Stderr, "Error parsing webhook JSON: %v\n", err) w.WriteHeader(http.StatusBadRequest) return } log.Printf("Subscription created for %d.", subscription.ID) // Then define and call a func to handle the successful attachment of a PaymentMethod. // handleSubscriptionCreated(subscription) case "customer.subscription.trial_will_end": var subscription stripe.Subscription err := json.Unmarshal(event.Data.Raw, &subscription) if err != nil { fmt.Fprintf(os.Stderr, "Error parsing webhook JSON: %v\n", err) w.WriteHeader(http.StatusBadRequest) return } log.Printf("Subscription trial will end for %d.", subscription.ID) // Then define and call a func to handle the successful attachment of a PaymentMethod. // handleSubscriptionTrialWillEnd(subscription) case "entitlements.active_entitlement_summary.updated": var subscription stripe.Subscription err := json.Unmarshal(event.Data.Raw, &subscription) if err != nil { fmt.Fprintf(os.Stderr, "Error parsing webhook JSON: %v\n", err) w.WriteHeader(http.StatusBadRequest) return } log.Printf("Active entitlement summary updated for %d.", subscription.ID) // Then define and call a func to handle active entitlement summary updated. // handleEntitlementUpdated(subscription) default: fmt.Fprintf(os.Stderr, "Unhandled event type: %s\n", event.Type) } w.WriteHeader(http.StatusOK) } require github.com/stripe/stripe-go/v85 v85.0.0 // This is a public sample test API key. // Don’t submit any personally identifiable information in requests made with this key. // Sign in to see your own test API key embedded in code samples. // Don't put any keys in code. See https://docs.stripe.com/keys-best-practices. StripeClient client = new StripeClient("<>"); PriceListParams priceParams = PriceListParams.builder().addLookupKey(request.queryParams("lookup_key")).build(); StripeCollection prices = client.v1().prices().list(priceParams); SessionCreateParams params = SessionCreateParams.builder() .addLineItem( SessionCreateParams.LineItem.builder().setPrice(prices.getData().get(0).getId()).setQuantity(1L).build()) .setMode(SessionCreateParams.Mode.SUBSCRIPTION) .setSuccessUrl(YOUR_DOMAIN + "/success.html?session_id={CHECKOUT_SESSION_ID}") .setSuccessUrl(YOUR_DOMAIN + "?success=true&session_id={CHECKOUT_SESSION_ID}") .setSubscriptionData(SessionCreateParams.SubscriptionData.builder().setTrialPeriodDays(7L).setBillingCycleAnchor(1672531200).build()) .setSubscriptionData(SessionCreateParams.SubscriptionData.builder().setBillingCycleAnchor(1672531200).build()) .addDiscount(SessionCreateParams.Discount.builder().setCoupon("{{COUPON_ID}}").build()) .setCustomer("cus_JyTTNqVDAoRYE1") .setCustomerAccount("acct_123") .setAutomaticTax( SessionCreateParams.AutomaticTax.builder() .setEnabled(true) .build()) .build(); Session session = client.v1().checkout().sessions().create(params); response.redirect(session.getUrl(), 303); com.stripe.param.billingportal.SessionCreateParams params = new com.stripe.param.billingportal.SessionCreateParams.Builder() .setCustomer(checkoutSession.getCustomer()) .setReturnUrl(YOUR_DOMAIN).build(); com.stripe.model.billingportal.Session portalSession = client.v1().billingPortal().sessions().create(params); com.stripe.param.billingportal.SessionCreateParams params = new com.stripe.param.billingportal.SessionCreateParams.Builder() .setCustomerAccount(checkoutSession.getCustomerAccount()) .setReturnUrl(YOUR_DOMAIN).build(); com.stripe.model.billingportal.Session portalSession = client.v1().billingPortal().sessions().create(params); response.redirect(portalSession.getUrl(), 303); return ""; post("/webhook", (request, response) -> { String payload = request.body(); Event event = null; try { event = ApiResource.GSON.fromJson(payload, Event.class); } catch (JsonSyntaxException e) { // Invalid payload System.out.println("⚠️ Webhook error while parsing basic request."); response.status(400); return ""; } String sigHeader = request.headers("Stripe-Signature"); if (endpointSecret != null && sigHeader != null) { // Only verify the event if you have an endpoint secret defined. // Otherwise use the basic event deserialized with GSON. try { event = client.constructEvent(payload, sigHeader, endpointSecret); } catch (SignatureVerificationException e) { // Invalid signature System.out.println("⚠️ Webhook error while validating signature."); response.status(400); return ""; } } // Deserialize the nested object inside the event EventDataObjectDeserializer dataObjectDeserializer = event.getDataObjectDeserializer(); StripeObject stripeObject = null; if (dataObjectDeserializer.getObject().isPresent()) { stripeObject = dataObjectDeserializer.getObject().get(); } else { // Deserialization failed, probably due to an API version mismatch. // Refer to the Javadoc documentation on `EventDataObjectDeserializer` for // instructions on how to handle this case, or return an error here. } // Handle the event Subscription subscription = null; switch (event.getType()) { case "customer.subscription.deleted": subscription = (Subscription) stripeObject; // Then define and call a function to handle the event // customer.subscription.deleted // handleSubscriptionTrialEnding(subscription); case "customer.subscription.trial_will_end": subscription = (Subscription) stripeObject; // Then define and call a function to handle the event // customer.subscription.trial_will_end // handleSubscriptionDeleted(subscriptionDeleted); case "customer.subscription.created": subscription = (Subscription) stripeObject; // Then define and call a function to handle the event // customer.subscription.created // handleSubscriptionCreated(subscription); case "customer.subscription.updated": subscription = (Subscription) stripeObject; // Then define and call a function to handle the event // customer.subscription.updated // handleSubscriptionUpdated(subscription); case "entitlements.active_entitlement_summary.updated": subscription = (Subscription) stripeObject; // Then define and call a function to handle the event // entitlements.active_entitlement_summary.updated // handleEntitlementUpdated(subscription); // ... handle other event types default: System.out.println("Unhandled event type: " + event.getType()); } response.status(200); return ""; }); 1. Build the server ~~~ pip3 install -r requirements.txt ~~~ 1. Build the server ~~~ bundle install ~~~ 1. Build the server ~~~ composer install ~~~ 1. Build the server ~~~ dotnet restore ~~~ 1. Build the server ~~~ mvn package ~~~ 2. Run the server ~~~ export FLASK_APP=server.py python3 -m flask run --port=4242 ~~~ 2. Run the server ~~~ ruby server.rb -o 0.0.0.0 ~~~ 2. Run the server ~~~ php -S 127.0.0.1:4242 --docroot=public ~~~ 2. Run the server ~~~ dotnet run ~~~ 2. Run the server ~~~ java -cp target/sample-jar-with-dependencies.jar com.stripe.sample.Server ~~~ 3. Build the client app ~~~ npm install ~~~ 4. Run the client app ~~~ npm start ~~~ 5. Go to [http://localhost:3000/checkout](http://localhost:3000/checkout) 3. Go to [http://localhost:4242/checkout.html](http://localhost:4242/checkout.html) 3. Build the client app ~~~ npm install ~~~ 4. Run the client app ~~~ npm start ~~~ 5. Go to [http://localhost:3000/checkout](http://localhost:3000/checkout) 3. Go to [http://localhost:4242/checkout.html](http://localhost:4242/checkout.html) 3. Build the client app ~~~ npm install ~~~ 4. Run the client app ~~~ npm start ~~~ 5. Go to [http://localhost:3000/checkout](http://localhost:3000/checkout) 3. Go to [http://localhost:4242/checkout.html](http://localhost:4242/checkout.html) 3. Build the client app ~~~ npm install ~~~ 4. Run the client app ~~~ npm start ~~~ 5. Go to [http://localhost:3000/checkout](http://localhost:3000/checkout) 3. Go to [http://localhost:4242/checkout.html](http://localhost:4242/checkout.html) 3. Build the client app ~~~ npm install ~~~ 4. Run the client app ~~~ npm start ~~~ 5. Go to [http://localhost:3000/checkout](http://localhost:3000/checkout) 3. Go to [http://localhost:4242/checkout.html](http://localhost:4242/checkout.html) 1. Run the server ~~~ go run server.go ~~~ 2. Build the client app ~~~ npm install ~~~ 3. Run the client app ~~~ npm start ~~~ 4. Go to [http://localhost:3000/checkout](http://localhost:3000/checkout) 1. Run the server ~~~ go run server.go ~~~ 2. Go to [http://localhost:4242/checkout.html](http://localhost:4242/checkout.html) 1. Build the application ~~~ npm install ~~~ 2. Run the application ~~~ npm start ~~~ 3. Go to [http://localhost:3000/checkout](http://localhost:3000/checkout) 1. Build the server ~~~ npm install ~~~ 2. Run the server ~~~ npm start ~~~ 3. Go to [http://localhost:4242/checkout.html](http://localhost:4242/checkout.html) ## 次のステップ #### [サブスクリプション料金の更新](https://docs.stripe.com/billing/subscriptions/change-price.md) サブスクリプションを更新して、顧客が料金プランをアップグレードまたはダウングレードできるようにします。 #### [比例配分の適用](https://docs.stripe.com/billing/subscriptions/prorations.md) 顧客の請求書を調整して、サイクル途中の料金体系の変更を正確に反映する方法をご紹介します。 #### [オファーのアップセル](https://docs.stripe.com/payments/checkout/upsells.md) 長期の請求期間に割引を適用することで、顧客にインセンティブを与えます。