Stripe でサブスクリプション請求や send_invoice を利用していると、次のような要件が出てくることがあります。
社内で採番した管理番号を、請求書に載せたうえで顧客に送付したい。一方で、Checkout を用いると、Stripe が生成する請求書が社内作業を待たずに自動的に finalize(発行)されてしまう。
この要件に対応しやすいのが Invoice の custom_fields と、ドラフトのまま自動発行を止める auto_advance: false です。以下では、draft で止める理由、Webhook と API をどう組み合わせるか、実装で外しやすい点を順にまとめます。
運用の前提: 確認者が行うのは、ドラフト請求書の内容確認と、問題なければ発行(finalize)の承認まで。それ以外——invoice.created 後の auto_advance: false や徴収まわりの整備、custom_fields への番号反映、承認後の finalize → send → 条件に応じた pay——はバックエンドが API で自動実行する想定です(Dashboard で手作業する前提ではありません)。
最初は Checkout を使おうとしていた
サブスクリプションや請求を Stripe で実装する場合、Checkout Session を最初に検討することは多いです。一方、社内で管理番号を振ってから請求書に載せるフローでは、請求書を draft のまま保持し、finalize の前に custom_fields を書く必要が出ます。
一般的な Checkout(サブスク+セッション完了時に即時決済まで進む構成)では、請求書がすみやかに finalize され支払いへ進みやすいため、この設計と相性が悪いです。Subscription API と請求書(Invoicing)を組み合わせ、invoice.created の Webhook で auto_advance: false を入れる構成へ寄せるのが現実的です。
実装の一例として、サブスクリプションを send_invoice で作成し、invoice.created の Webhook で請求書の徴収まわりや PDF 設定などをまとめて整える、という形もよく取られます。
注意: 2026 年 3 月時点の Stripe のドキュメント・挙動を前提とします。実装時は 公式ドキュメント を必ず確認してください。
要点
- 管理番号を請求書に載せる → 多くの場合
custom_fields(PDF / Hosted Invoice Page に表示) - finalize 後は変更しにくい → draft のうちに書く
- 新規サブスク+
charge_automatically→ 初回請求は即 finalize になりやすく、draft で触る猶予がない(Subscription invoices)。send_invoiceやauto_advance: falseなどの設計とセットで検討 - 自動 finalize を止める →
invoice.createdでauto_advance: false - 明細行の追加は
invoiceitems、顧客に見せないキーはmetadata(役割が別) - 確認者は確認・発行承認まで。それ以外は API で自動(上記の運用前提)
運用上の処理の流れをフロー図にすると、次のとおりです。
用語と API の対応関係
「請求書に項目を追加したい」という要求は、Stripe 上では複数の API に分かれます。混同すると実装が複雑化しやすいため、先に対応関係を示します。
| やりたいこと | Stripe 上の主な手段 | タイミング |
|---|---|---|
| 管理番号を請求書に印字 | custom_fields |
draft のみ(finalize 後は困難) |
| 金額の行を足す |
invoiceitems.create(invoice に draft ID) |
finalize 前のみ |
| 連携用キー(PDF に出さない) |
metadata(Invoice / Customer) |
API 向け |
なぜ「発行後に追記」が難しいのか
サブスクリプション請求の Invoice は、いったん draft で作成されるところから始まります。ただし collection_method や初回/更新によって、draft のまま編集できる時間は同じではありません。一般論として open(発行済み)になると custom_fields を含め実質ロックに近いため、確認者が番号を振ってから PDF に載せるには、draft の間に custom_fields を書く必要があります。
新規サブスクの初回請求と collection_method(公式の finalize タイミング)
Stripe の Subscription invoices(New subscription invoices)では、サブスクリプション作成時に作られる請求書の扱いが collection_method ごとに次のように説明されています。
-
charge_automatically: 請求書をすぐに finalize し、顧客のデフォルト決済手段への請求に進みます。 -
send_invoice: finalize は最大で約 1 時間後(その間は請求書を編集できる、という説明)。
同ページの「Charge default payment method」には、初回請求は即 finalize され、finalize 前に初回請求を更新できる1 時間の猶予はないことも明記されています。
つまり新規サブスク+charge_automatically では、初回請求が即 open 側へ進み、draft のまま custom_fields を載せる時間が事実上取れないことがあります。要件が「初回の PDF にも管理番号を載せたい」であれば、send_invoice に寄せる、invoice.created で即 auto_advance: false など draft を維持する別策を講じる、公式が案内するトライアル等で初回請求の扱いを調整するなど、初回だけ挙動が違う前提で設計する必要があります。
更新請求と Webhook まわり
更新請求(例: billing_reason が subscription_cycle)では、Subscription invoices ではしばらく draft のままになり、その後 finalize・徴収に進む、と説明されています。auto_advance が有効なままだと、draft が長く留まらず自動 finalize に進むリスクは引き続きあるため、invoice.created で auto_advance: false にする運用と相性がよい場面があります。
Webhook の遅延や障害があると、その間に自動 finalize が走るリスクがあるので、Handler は速やかに 2xx を返し、更新処理は何度来ても同じ結果になるよう(冪等に近く)しておくと安全です。
draft を止める: auto_advance: false
draft の Invoice に auto_advance: false を入れると、自動 finalize の進行を抑止できます。
| 方法 | 内容 |
|---|---|
| Webhook |
invoice.created かつ status === 'draft' のとき invoices.update で auto_advance: false
|
| ダッシュボード等 | 請求書をレビュー用に保留(公式) |
コード例(Node.js / stripe)
本番ではリクエストボディの生データで 署名を検証してから処理します(stripe.webhooks.constructEvent)。Stripe は失敗時に同じイベントを再送するため、処理完了後は 2xx を返し、invoices.update は何度実行してもよい形にしておきます。
import Stripe from 'stripe'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
export async function handleInvoiceCreated(invoice: Stripe.Invoice) {
if (invoice.status !== 'draft') return
await stripe.invoices.update(invoice.id, {
auto_advance: false,
})
}
実装によっては、同じ invoices.update に PDF・税・支払期限・徴収方法などもまとめて載せます。
全体の流れ
確認・承認以外は API で自動とした場合の、処理の順序の一例です。
| 順 | 誰が | 何をするか |
|---|---|---|
| 1 | Webhook(自動) |
invoice.created → auto_advance: false 等で draft を保持 |
| 2 | バックエンド(自動) | 必要なら invoiceitems、採番・取り込み後に custom_fields 更新 |
| 3 | 確認者 | ドラフトの内容確認と発行承認(自社画面) |
| 4 | バックエンド(自動) |
finalize → send → 課金設計に応じて pay
|
draft の特定(自動処理で誤更新しないために): invoices.list で subscription + status: 'draft' などで絞り、請求ラインの period.end(Unix 秒)が対象の請求期間と一致するものを選ぶ、といったルールを一文で決めておくとよいです。
pay の有無:
| パターン | pay |
|---|---|
| カードで発行後に徴収する設計 | 呼ぶことが多い(Pay an invoice) |
| 日本の銀行振込(仮想口座・請求書フロー) | 呼ばないことが多い(Bank transfer (JP)) |
custom_fields の要点
- 最大 4 件、名前 40 文字・値 140 文字まで(Invoice オブジェクト)
-
draft のみ更新可能。PDF と Hosted Invoice Page の両方に出る前提でラベル(例:
管理番号)を決める
マージしながら更新する例(Node.js)
既存の custom_fields を消さないよう、retrieve → マージ → update します。
import Stripe from 'stripe'
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
async function setInternalMgmtNumberOnDraft(
invoiceId: string,
fieldName: string,
fieldValue: string,
) {
const inv = await stripe.invoices.retrieve(invoiceId)
if (inv.status !== 'draft') {
throw new Error(`Invoice is not draft: ${inv.status}`)
}
const existing = inv.custom_fields ?? []
const others = existing.filter((f) => f.name !== fieldName)
const custom_fields = [...others, { name: fieldName, value: fieldValue }].slice(
0,
4,
)
return stripe.invoices.update(invoiceId, { custom_fields })
}
await setInternalMgmtNumberOnDraft('in_xxx', '管理番号', 'INT-2026-0042')
テスト
Stripe CLI の stripe listen --forward-to で Webhook をローカルに転送する方法は 公式 を参照。テストモードで Subscription を作り、実際に invoice.created が届くところまで通すのが確実です。
付録: 一覧から一括で custom_fields を載せる場合
- 社内キーと Stripe Customer を対応付ける(Customer
metadataがよく使われる) - 対象期間の draft Invoice を特定する(
period.end等) - 行ごとに
invoices.update(custom_fields) - 結果をログ・通知で返す
秘密鍵・Webhook シークレット・ファイル経路の認可は本番前に設計・レビューしてください。
まとめ
- 管理番号 →
custom_fields+ draft - 新規サブスクの初回+
charge_automatically→ 公式どおり即 finalize になり、draft でcustom_fieldsを書く時間が取れないことがある →send_invoiceや抑止策を設計に含める - 自動発行の抑止 →
invoice.createdでauto_advance: false(署名検証・2xx・再送を意識) - 確認者は確認・承認まで、
finalize/send/payは承認後に API で自動 - Checkout 一択は draft 猶予の設計と相性が悪いことが多い → Invoicing 寄りの構成を検討