# 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}}
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)
長期の請求期間に割引を適用することで、顧客にインセンティブを与えます。