Stripe Billingでサブスクを組むとき、最初に決めるのはPriceの持ち方と、Webhookで何を自社DBへ写すかの2点です。本記事では、Priceの作成、Checkoutでの申込と利用権の同期、Billing Metersによる従量課金、カスタマーポータル、日割り計算までをNode.jsのコードで示します。2025-03-31.basilで削除された使用量レコードなど、古い解説のままでは動かない箇所も扱います(2026年10月時点の公式ドキュメントで確認)。
まとめ|Stripe Billingのサブスク実装で先に決める設計と料金の要点
Stripe Billingは、日本では設定手数料も月額料金もなく、Billing取引額の0.7%がカード手数料3.6%に上乗せされる料金体系です。Priceはlookup_keyで引けるように作り、金額の改定は新しいPriceを足して付け替えます。申込はCheckoutのmode: 'subscription'で受けるのが最短で、利用権はリダイレクトではなくWebhookで更新します。
従量課金はBilling Metersでメーターを作ってイベントを送る方式に一本化されました。ただしStripeは新規の従量課金にMetronomeを推奨しており、前払いクレジットやコミット契約が要る案件はそちらを検討します。解約とカード変更はカスタマーポータルに任せられますが、従量課金や複数商品を含むサブスクはポータルでプラン変更ができません。自社の料金表がこれに当たるかは実装前に確かめてください。
Stripe Billingの守備範囲:サブスクの請求を組むオブジェクトと日本の料金
Billingは単体の画面ではなく、Product・Price・Subscription・Invoiceを組み合わせて継続請求を回す仕組みの総称です。定義や他の決済方式との位置付けはStripeとは?決済プラットフォームの仕組み・手数料・実装パターンを開発視点で解説で整理しているので、ここではサブスク実装に要る部分だけを扱います。
Product・Price・Subscription・Invoiceの請求上の役割
Productが持つのは「何を売るか」、Priceが持つのは「いくらで、どの周期で」という情報です。顧客にPriceを紐付けたものがSubscriptionで、周期が来るたびにStripeがInvoiceとPaymentIntentを作って決済を試みます。Stripeのサブスクリプション概要によると、即時決済のサブスクは作成直後にincompleteとなり、初回請求書が支払われるとactiveへ移ります。
注意したいのは、23時間以内に初回の支払いが成功しないとincomplete_expiredになり、請求書は無効化される点です。このサブスクは復活できず、新たなサブスクの作り直しが必要です。申込画面を離れた顧客に後日支払いを促す設計は取れないので、未完了の申込は「やり直してもらう」前提でUIを作ります。継続課金そのものの設計論はサブスクリプション課金とは?継続課金の仕組みと更新失敗対策に譲ります。
Billing手数料0.7%とカード3.6%を月額3,000円の契約で試算する
Stripeの日本向け料金ページでは、Billingは「Billing取引額のうち0.7%」で、Stripe以外で処理されたBilling取引も対象に含み、1回限りの請求書は除外されます。月額料金はかかりません。カード決済は別途3.6%です。
| 月額3,000円の契約1件 | 料率 | 1か月あたり |
|---|---|---|
| カード決済手数料 | 3.6% | 108円 |
| Billing手数料 | 0.7% | 21円 |
| 合計 | 4.3% | 129円 |
1,000契約なら月12万9,000円です。Metronomeで従量課金を組むなら請求額の0.8%と取り込み1,000イベントあたり0.04ドルが別建てになります。料金は改定されるため、見積もりの直前に同ページで確かめてください。
ProductとPriceを作る手順:lookup_keyで月額・年額プランを引く設計
PriceのIDをコードや環境変数に直書きすると、金額改定のたびにデプロイが要ります。lookup_keyを付けておけば、新しいPriceへキーを付け替えるだけで申込側のコードは変わりません。
円建ての月額・年額Priceをlookup_key付きでNode.jsから作るコード
Stripe公式のNode.jsライブラリは2026年10月時点で23系です。v12以降のstripe-nodeは、そのリリース時点のAPI版でリクエストを送ります。円は小数点以下のない通貨なので、3,000円は3000と書きます(100倍しない)。初期データの投入用に一度だけ流すスクリプトの例です。
// seed-prices.mjs(初回だけ実行する)
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const product = await stripe.products.create({ name: 'Proプラン' });
for (const [interval, amount, key] of [
['month', 3000, 'pro_monthly'],
['year', 30000, 'pro_yearly'],
]) {
await stripe.prices.create({
product: product.id,
currency: 'jpy',
unit_amount: amount, // 円はそのままの値
recurring: { interval },
lookup_key: key,
transfer_lookup_key: true, // 既存Priceからキーを移す
});
}
transfer_lookup_key: trueを付けると、同じキーを持つ旧Priceからキーが外れて新Priceへ移ります。値上げ時は金額を変えて再実行すれば、既存契約は旧Priceのまま、新規申込だけが新Priceになります。
同じ商品と課金間隔に複数Priceを置けないポータルの制約と商品の分け方
カスタマーポータルの制限事項には、同じproductとrecurring.intervalの組み合わせに複数のPriceを定義できない、とあります。通常価格と学割を同じ月額で並べたいなら、学割用のProductを別に作ります。
ポータルでプラン変更を許す場合、選択肢として指定できるのは最大10製品です。プランを細かく刻んだ料金表は、ポータルの変更画面に収まりません。Product=ポータルに並ぶプラン、と考えて数を決めるのが安全です。
Checkoutでサブスクを始めWebhookで利用権を同期する実装の流れ
申込画面をStripeのホスト型Checkoutに任せると、カード入力と3Dセキュアの処理を書かずに済みます。Checkoutのui_modeや埋め込み型の実装はStripe Checkoutの実装方法:ui_mode別のセッション作成とWebhookでの注文確定で扱ったので、ここではサブスク特有の箇所に絞ります。
mode=subscriptionのセッション作成とbilling_modeの既定値
料金表のボタンからlookup_keyを受け取り、Priceを引いてセッションを作ります。顧客IDは会員登録時に作って自社DBに持たせておくと、後でポータルを開くときに同じIDを使えます。
app.post('/billing/checkout', express.urlencoded({ extended: false }), async (req, res) => {
const { data } = await stripe.prices.list({ lookup_keys: [req.body.lookup_key] });
const session = await stripe.checkout.sessions.create({
mode: 'subscription',
customer: req.user.stripeCustomerId,
line_items: [{ price: data[0].id, quantity: 1 }],
subscription_data: { trial_period_days: 14 },
success_url: 'https://example.com/account?session_id={CHECKOUT_SESSION_ID}',
cancel_url: 'https://example.com/pricing',
});
res.redirect(303, session.url);
});
請求モードの説明によれば、API版が2025-09-30.clover以降なら新しいサブスクのbilling_modeは既定でflexibleです。flexibleは日割りやトライアルの計算が旧来のclassicと異なり、一度flexibleにしたサブスクはclassicへ戻せません。古いAPI版から上げる場合は、既存サブスクとの計算差を先に確かめます。コードを書かずにサブスクの申込リンクを作る方法はStripe Payment Linksの使い方で解説しています。
invoice.paidとsubscriptionイベントによる利用権の同期処理
Webhookの到着順は保証されず、同じイベントが複数回届くこともあるとStripeのWebhookガイドは書いています。そこでイベント本文の値を信じず、サブスクを取り直して状態をそのまま写す形にします。
app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
let event;
try {
event = stripe.webhooks.constructEvent(
req.body, req.headers['stripe-signature'], process.env.STRIPE_WEBHOOK_SECRET);
} catch {
return res.status(400).send('invalid signature');
}
if (await isProcessed(event.id)) return res.sendStatus(200);
let subId = null;
if (event.type.startsWith('customer.subscription.')) {
subId = event.data.object.id;
} else if (event.type === 'invoice.paid' || event.type === 'invoice.payment_failed') {
subId = event.data.object.parent?.subscription_details?.subscription ?? null;
}
if (subId) {
const sub = await stripe.subscriptions.retrieve(subId);
const item = sub.items.data[0];
await upsertEntitlement({
customerId: sub.customer,
subscriptionId: sub.id,
status: sub.status,
plan: item.price.lookup_key,
periodEnd: item.current_period_end, // basil以降はitem側にある
});
}
await markProcessed(event.id);
res.sendStatus(200);
});
古い記事のコードが動かなくなる典型は2か所です。2025-03-31.basilの変更で、current_period_endはSubscriptionから消えてSubscriptionItem側へ移りました。請求書からサブスクIDを引く場所もparent.subscription_details.subscriptionに変わっています。署名検証と冪等処理の考え方は決済Webhookとは?署名検証と冪等な注文確定の実装を参照してください。
past_due・unpaid・canceledで利用権を止める判断基準と状態表
アクセス可否はステータスで決めます。past_dueの後に何へ移るかは、ダッシュボードの失敗した支払いの設定でcanceled・unpaid・past_dueのまま、から選びます。
| status | 状態 | 利用権の扱い(推奨) |
|---|---|---|
| trialing | トライアル中 | 許可 |
| active | 正常 | 許可 |
| incomplete | 初回未払い(23時間) | 付与しない |
| past_due | 更新の決済失敗・再試行中 | 許可しつつカード更新を促す |
| unpaid | 再試行終了後も未払い | 停止 |
| canceled | 解約済み(更新不可) | 停止 |
| paused | カード未登録でトライアル終了 | 停止 |
past_dueで即停止すると、カードの一時的な拒否で正常な顧客まで締め出します。再試行の期間は猶予として扱い、unpaidかcanceledに移った時点で止める運用が無難です。トライアル終了の3日前にはcustomer.subscription.trial_will_endが届くので、カード未登録の顧客への案内はこのイベントで送ります。カード拒否や3Dセキュアの経路はStripeテストカード一覧と使い方の番号で再現できます。
Billing Metersで従量課金を記録する:メーター作成からイベント送信まで
API呼び出し数やAIのトークン数で課金する場合、使用量をStripeへ送り、請求期間の終わりに集計させます。2025年3月以降、この方式はBilling Metersに一本化されています。
メーターと従量Priceの作成、basilで削除された使用量レコードとの違い
2025-03-31.basilの互換性に関わる変更で、UsageRecordのAPIとaggregate_usageは削除されました。usage_type: 'metered'のPriceはメーターの指定なしでは作れません。createUsageRecordを使う記事のコードは、新しいSDKではそのまま動きません。
const meter = await stripe.billing.meters.create({
display_name: 'API呼び出し回数',
event_name: 'api_requests',
default_aggregation: { formula: 'sum' },
customer_mapping: { type: 'by_id', event_payload_key: 'stripe_customer_id' },
value_settings: { event_payload_key: 'value' },
});
await stripe.prices.create({
product: product.id,
currency: 'jpy',
unit_amount: 2, // 1回あたり2円
recurring: { interval: 'month', usage_type: 'metered', meter: meter.id },
lookup_key: 'api_metered',
});
メーター作成APIのリファレンスでは、event_nameは100字まで、default_aggregationは必須です。この従量Priceを定額Priceと同じサブスクのline_itemsに並べれば、基本料+従量のハイブリッドになります。料金体系の設計そのものは従量課金とは?使用量計測から請求生成までの仕組みで解説しています。
meter_eventsの送信コードと35日前〜5分後のタイムスタンプ制約
使用量はイベントとして送ります。発生のたびに送ると上限に当たりやすいので、自社側で1時間ごとに合計してから送る形にしています。
// 1時間ごとのバッチで、顧客ごとの合計回数を送る
await stripe.billing.meterEvents.create({
event_name: 'api_requests',
payload: { stripe_customer_id: customerId, value: String(count) },
identifier: `${customerId}-${hourKey}`, // 同じ時間枠の二重送信を防ぐ
timestamp: hourEndUnix,
});
使用量記録APIのガイドが示す制約は次のとおりです。
- timestampは35日前から5分後までしか受け付けない
- 本番のMeter Eventは1アカウント毎秒1,000回まで、さらにメーター×顧客ごとに同時1回
- v2のメーターイベントストリームなら毎秒最大10,000件(認証トークンの有効期限は15分)
- 請求書の確定後に届いた使用量は、その請求にも次の請求にも載らないことがある
実務で効くのは最後の項目です。送信バッチは請求の確定より前に送り切る時刻へ寄せてください。送信エラーは非同期でv1.billing.meter.error_report_triggeredとして届くため、これも監視します。
新規の従量課金はMetronome推奨、基本のMetersで足りる条件
Stripeの従量課金のドキュメントは、Metronomeを主要な従量課金プラットフォームと位置付け、新規実装にはMetronomeを推奨しています。前払いクレジット、エンタープライズのコミット契約、複合的な料金、大量のイベント取り込みが要るならMetronomeの領域です。
一方、既存のBilling Metersから移行する必要はなく、基本の従量課金はConnectやCheckoutとの互換性が高いとも書かれています。「回数×単価」に基本料を足す程度で、Checkoutで申込を受けるSaaSなら、Billing Metersで十分です。料率も、Metronomeが請求額の0.8%+イベント課金なのに対し、Billingは0.7%に収まります。
カスタマーポータルの組み込み:セッション発行とプラン変更の日割り制御
解約・カード変更・請求書の閲覧は、どのサブスクでも要る画面です。カスタマーポータルを使えば自前で作らずに済みます。
billing_portal.sessionsとflow_dataで開く管理画面
マイページの「契約の管理」ボタンから、次のエンドポイントへPOSTさせます。ポータルの機能(解約の可否、変更できるプラン)はダッシュボードの設定で決めます。
app.post('/billing/portal', async (req, res) => {
const session = await stripe.billingPortal.sessions.create({
customer: req.user.stripeCustomerId,
return_url: 'https://example.com/account',
});
res.redirect(303, session.url);
});
発行したURLは使わなければ5分で失効し、使い始めた後は最後の操作から1時間で失効します。都度発行する作りにし、URLをメールに貼るような使い方はしません。iframeの中には表示できません。
特定の操作だけを開くにはflow_dataを渡します。ポータルのディープリンクで指定できるtypeはpayment_method_update・subscription_cancel・subscription_update・subscription_update_confirm・customer_updateの5種です。past_dueの顧客にはpayment_method_updateで直接カード更新画面を開かせると、迷わせずに済みます。
プラン変更の日割り:proration_behaviorの3値と請求プレビューの使い方
期間の途中で月額3,000円から5,000円に上げると、旧プランの未使用分がクレジット、新プランの残り期間が請求として計上されます。日割り(比例配分)のドキュメントによると計算は秒単位で、挙動はproration_behaviorで切り替えます。
create_prorations(既定):日割り項目を作り、原則は次回の請求書に載せるalways_invoice:日割りを計算して、その場で請求書を発行するnone:日割りせず、次回から新料金で全額請求する
アップグレードは即時課金したい、ダウングレードは次回から、という要件が多いので、変更の向きでこの値を出し分けます。変更前に金額を見せるときはstripe.invoices.createPreviewにsubscription_details.proration_dateを渡し、本番の更新でも使うのは同じproration_dateの値です。秒単位の計算なので、これを揃えないと画面の表示額と実際の請求額がずれます。従量課金の部分は日割りの対象外です。
ポータルで変更できない従量課金・複数商品サブスクの自作による代替手段
ポータルの制限事項には、複数の商品・従量課金・トライアルオファー・請求書送付(send_invoice)を含むサブスクは、ポータルでキャンセルはできても更新はできない、と明記されています。基本料+従量のハイブリッドはここに該当します。
確定画面だけをポータルに任せるsubscription_update_confirmのフローも、指定できる項目は1つで、複数の項目を持つサブスクは更新できません。基本料+従量の構成では、プラン変更の画面と確定処理を自前で作ります。サーバー側でcreatePreviewの金額を見せ、同意を得てからstripe.subscriptions.updateで定額側の項目だけPriceを差し替える流れです。解約とカード変更はポータルのまま使えるので、自作するのは変更画面1枚で済みます。
Stripe Billingを採用する条件と自前の請求基盤を選ぶ場面の判断
ここまでの仕様を踏まえると、Billingが向く案件と向かない案件の境目はかなりはっきりしています。
Billingで足りる条件:カード決済の月額・年額・座席数・単純な従量
カード決済が主で、料金表が「月額か年額」「座席数(quantity)」「回数×単価」の組み合わせに収まるSaaSなら、Billingを採用します。本記事の構成なら、請求まわりで自前コードが必要なのはWebhook受信とメーター送信です。1契約あたり月21円の手数料は、督促や再試行を自作する工数より安く収まります。
サブスク管理の専用SaaSとの比較軸はサブスクリプション管理システムの比較軸|料金の増え方・連携・自社開発の判断で整理しています。
Billingを見送る場面:請求書払い中心の法人取引と決済手段の制約
次の条件に当てはまるなら、Billingを請求の中心に置くのは見送ります。
- 取引の大半が月末締め・翌月末振込の請求書払いで、請求書の発行元が既に会計システム側にある
- 口座振替やコンビニ払いでの継続課金が主(ポータルの対応決済手段の一覧にこれらは無い)
- 前払いクレジットや年間コミットなど、Stripe自身がMetronomeを推奨する料金体系
- ポータルで変更できない複合プランが多く、変更画面の自作が避けられない
前2つに当たる企業では、Stripeの請求書と会計側の請求書が二重になり、入金消込の突合が手作業で残ります。この場合はカード決済だけをStripeで受け、契約と請求の台帳は自社システムに持つ構成が運用面で有利です。どこまでをStripeに任せ、どこから基幹システムと連携させるかの線引きを含めて設計を相談したい場合は、決済・サブスクリプションシステム開発で要件整理から対応しています。
Stripe Billingのよくある質問|料金・従量課金・ポータル・旧コード
Stripe Billingの実装で検索されやすい疑問に、2026年10月時点の公式ドキュメントに基づいて答えます。
Stripe Billingの料金はいくらかかりますか?
日本の料金ページでは、Billing取引額の0.7%で、設定手数料や月額料金はありません。カード決済の3.6%は別にかかるため、カード払いのサブスクなら合計4.3%が目安です。Metronomeで従量課金を組む場合は料率が別建てになります。
createUsageRecordを使った従量課金の古いコードはまだ動きますか?
2025-03-31.basil以降のAPI版では動きません。UsageRecordのAPIとaggregate_usageが削除され、メーターを指定しない従量Priceは作成もサブスクへの紐付けもできなくなりました。Billing Metersでメーターを作り、billing.meterEvents.createで使用量を送る形に書き換えます。Stripeは、SDKを上げつつ旧API版を固定して生のリクエストで呼ぶ移行期の手段も案内していますが、新規実装では使いません。
カスタマーポータルは日本語で表示できますか?
表示できます。ポータルは日本語(ja)に対応し、顧客の優先言語やブラウザーの言語設定で自動的に切り替わります。セッション作成時のlocaleで固定することも可能です。ロゴや色もダッシュボードで設定できます。
subscription.current_period_endが取れなくなったのはなぜですか?
2025-03-31.basilで、請求期間がサブスク全体ではなく各サブスク項目ごとに管理されるよう変わったためです。current_period_startとcurrent_period_endはSubscriptionから削除され、items.data[].current_period_endへ移りました。利用期限を自社DBに保存しているコードは、サブスク項目側の値を読むように直します。customer.subscription.updatedのprevious_attributesの見え方も変わっています。
無料トライアル付きのサブスクはどう設定しますか?
Checkoutならsubscription_data.trial_period_daysに日数を渡します。トライアル中のステータスはtrialingで、終了の3日前に届くのはcustomer.subscription.trial_will_endというイベントです。カードを取らずに始めた場合、トライアル終了時に決済手段が無ければpausedにする設定ができます。なお、トライアル中の顧客がポータルでプランを変えるとトライアルが終わり即時請求になるため、継続させたいならポータル設定でcontinue_trialを指定します。
関連記事
- Stripeとは?決済プラットフォームの仕組み・手数料・実装パターンを開発視点で解説:Billingを含むStripe全体の位置付けと手数料。
- Stripe Checkoutの実装方法:ui_mode別のセッション作成とWebhookでの注文確定:サブスクの申込画面に使うCheckoutの実装。
- サブスクリプション課金とは?継続課金の仕組み・オフセッション決済と更新失敗対策の実装を開発視点で解説:Stripeに依らない継続課金の設計論。
- 従量課金とは?使用量計測から請求生成までの仕組みと実装の勘所を開発視点で解説:メータリングと単価設計の考え方。
- Stripe APIの使い方:PaymentIntentの作成・APIキー認証・冪等キーをNode.jsとcurlで実装:APIキーの扱いと冪等キーの基本。