Q1決済代行サービスを使って支払いを受けるとき、カードの情報はどこを通りますか。
課金の仕組み — Stripe が代行していること・ Webhook で権利を付ける
この記事は、プログラミングやバイブコーディングをする上で最低限身につけたほうがよい IT の実践的な知識を 1 から身につける「IT 基礎知識講座の一部」です。
カードの情報は決済代行サービスで止まり、自分のサーバーを通りません。有料の機能を出す根拠が Webhook の通知になる理由を図解で確かめます。
この記事は、課金の仕組みを扱います。
カードの情報を自分のサーバーに届かせない仕組みと、支払いの結果を自分のアプリに反映する仕組みの 2 つに分かれます。
カード番号は上の枝で止まり、my-app には届きません。
有料の機能を出す根拠になるのは、下の枝で届く通知だけです。
カード番号の受け取りもカード会社とのやり取りも決済代行サービスの側で行われ、my-app に残るのは結果を記録に書く部分だけです。
カードの情報を受け取るのは決済代行サービスで、自分のサーバーではない
支払いの処理には、カードの番号・有効期限・セキュリティコードが要ります。
カード情報を扱う事業者が守る基準がPCI DSS(Payment Card Industry Data Security Standard)、カード決済を代行する外部サービスが決済代行サービス(payment service provider)です。
決済代行サービスは Stripe や PayPal が代表です。
入力欄をどこに置くかで、カード番号が通る場所が変わります。
| 入力欄の置き場所 | カード番号が通る場所 | PCI DSS の対象 |
|---|---|---|
| my-app で受け取って保存 | my-app と決済代行サービス | my-app も対象 |
| my-app で受け取って転送 | my-app と決済代行サービス | my-app も対象 |
| 決済代行サービスの画面 | 決済代行サービスだけ | my-app の範囲は最小 |
3 行目だけが、カード番号を my-app のサーバーに通していません。
このとき my-app に残るのは支払いを見分ける値だけで、上の 2 行ではカード番号そのものや通信の記録が残ります。
保存せず転送するだけでも値は自分のサーバーを通り、通った時点で PCI DSS の対象範囲に my-app も入ります。
my-app 側で守る範囲をいちばん小さくするため、入力欄ごと決済代行サービスに預けます。
- カードの番号・有効期限・セキュリティコード
- カード会社とのやり取りの記録
- 支払いを見分ける値 pay_88
- 金額と、支払いが済んだかどうか
- どの利用者の支払いか(u_1024)
カード番号は my-app を通らない
カード番号を自分で受け取らなければ、それを守る責任も自分の側には来ません。
保存しても、すぐ転送しても自分のサーバーを通せば同じ責任がかかるので、入力欄ごと決済代行サービスに預けて、my-app に届くのを支払いが済んだという事実だけにします。
支払いは、決済代行サービスのドメインにある決済ページで終わる
カードの情報を自分のサーバーに通さないよう、入力する画面そのものを別の場所に置いたものがホスト型の決済ページ(hosted checkout page。決済代行サービスが用意し、そのドメインで表示される支払いの画面)です。
別のドメインの画面へ利用者を移す動きがリダイレクト(redirect)です。
2 段階目で渡すのは金額と戻り先で、決済代行サービスはその支払い専用の決済ページの URL を返します。
この受け渡しは Web API を呼んで行い、API キーを添えます。
- 有料プランの申し込み画面
- 支払いのあとに開く /thanks
- カード番号を入力する決済ページ
- この画面の中身は my-app からは見えない
決済ページは、my-app の画面へ枠として置く形でも提供されます。
見え方は変わりますが、入力された値の行き先は同じです。
決済ページは決済代行サービスが用意する
カード番号を入力する画面は、my-app ではなく決済代行サービスが持っています。
利用者はリダイレクトでその画面へ移り、支払いが終わると /thanks に戻りますが、画面に枠として置く形にしても、入力された値の行き先は変わりません。
権利を付ける根拠は、戻ってきた画面ではなく Webhook の通知
有料の機能を出してよいかを決めるのは決済代行サービスではなく my-app で、その記録が権利(entitlement。支払いを済ませた利用者が使える機能の範囲を記録したもの)です。
利用者の画面が /thanks に戻ってきただけでは、my-app は支払いが済んだかを知りません。
支払いのあとに起きることを、2 つの道に分けて見てください。
上の道は、利用者が支払いの直後にブラウザを閉じれば、そこで止まります。
下の道はサーバー同士なので、届かなければ決められた回数と期間のあいだ通知がやり直されます。
どちらを根拠にするかで、同じ 3 人でも権利が付く相手が変わります。
| その利用者に起きたこと | /thanks で判断すると | /webhook で判断すると |
|---|---|---|
| 支払って /thanks も開いた | 権利が付く | 権利が付く |
| 支払ったあとすぐ閉じた | 権利が付かない | 権利が付く |
| 支払わずに /thanks を開いた | 権利が付く | 権利が付かない |
/thanks で判断すると、閉じた人には付かず、支払っていない人には付いてしまいます。
権利を付ける根拠にするのは、下の道の通知だけです。
決済では、同じ通知を 2 回反映すると、使える期限が二重に延びてしまいます。
通知を見分ける値を記録しておき、2 回目の通知では権利を変えません。
署名が合わない通知は、権利を変えずに捨てます。
残る 2 つの違いは、その通知をすでに反映したかどうかだけです。
- 利用者を見分ける値 u_1024
- パスワードのハッシュ
- プランの種類(paid)
- 使える期限
- どの利用者のものか(u_1024)
- 通知を見分ける値 evt_7
- 反映した日時
画面で有料のボタンを隠しても、そのコードは利用者の端末に届きます。
サーバー側で毎回権利を確かめるのは、ログイン機能の仕組みで扱った認可と同じです。
カード番号は真ん中の囲みの中で止まり、右の囲みには入りません。
権利が付くのは矢印 2 の通知が届いたときで、利用者のブラウザが戻ってきたときではありません。
支払いを知らせるのはサーバーからの通知
支払いが済んだことを知らせるのは、利用者が戻ってきた画面ではなく、決済代行サービスのサーバーから届く通知です。
届いたものは署名を照合し、すでに反映した通知なら権利を変えず、その結果を自分のデータベースに書いて、有料の機能を出すかどうかを決めます。
サブスクリプションでは、期間ごとに通知が届いて権利が変わる
月ごとや年ごとに料金がかかる契約がサブスクリプション(subscription。決められた期間ごとに支払いが自動で繰り返される契約)です。
繰り返される支払いが、毎回成功するとは限りません。
失敗の行では、決済代行サービスが日を置いて請求をやり直します。
その間も使える状態を残すか、すぐ止めるかは my-app が決めます。
公開する前に確かめられるのがテストモード(test mode。実際の請求を起こさずに動作を確かめるための、決済代行サービスが用意した状態)です。
テスト用と本番用で API キーが分かれているので、環境変数の値で切り替えます。
権利を外す通知まで決めてから公開する
サブスクリプションでは期間が来るたびに支払いが行われ、そのたびに通知が届きます。
付ける通知だけを処理すると支払いが止まった利用者が使えるまま残るので、付ける・延ばす・外すの 3 つを届く通知ごとに決め、公開の前にテストモードで一通り通しておきます。
理解度チェック
まずは1問ずつ答えてみましょう。
Q2支払いが済んだと判断して利用者に権利を付ける根拠に使うのはどれですか。
Q3継続課金で支払いの失敗を知らせる通知が届いたとき、自分のアプリが行うのはどれですか。