今回はこの機能について調べた。

Stripe CLI

Stripeは開発のためのいくつかの機能をCLIとして提供している1

インストール

Homebrewからインストールできる。

brew install stripe/stripe-cli/stripe

またその他のインストール方法についてもドキュメントに記載されている。

ログイン

stripe-cliをインストールするとstripeコマンドが使えるようになる。 まずはstripeコマンドをログイン状態にする。

stripe login

実行するとURLが表示される。Enterキーを入力するとWebブラウザに遷移し、 Stripeへのログインを求められる。

Your pairing code is: XXXXX-XXXXXX-XXXXXXXXX-XXXXXX
This pairing code verifies your authentication with Stripe.
Press Enter to open the browser or visit https://dashboard.stripe.com/stripecli/confirm_auth?t=XXXXXXXXXXXXXXXXXXXXXXXX (^C to quit)

ログインするとペアリングコードを確認するメッセージが表示される。ターミ ナル上に表示されているペアリングコードと同じかどうかを目視で確認し、同 じであれば許可ボタンをクリックする。

⣻ Waiting for confirmation... > Done! The Stripe CLI is configured for example with account id XXXX_XXXXXXXXXXX
Please note: this key will expire after 90 days, at which point you'll need to re-authenticate.

ログイン処理が完了すると ~/.config/stripe/config.toml が作成され、その中にAPIキーなどが記述される。 ここで登録されるAPIキーの有効期限は90日である。

Stripeで発生するイベントをWebhookで受け取る

決済サービスのStripeにはWebookの機能があり、イベントが発生するとhttps経由で通知する2

Stripel CLIを用いてイベントをWebhookとして受信する

Webhookはインターネットにエンドポイントを公開し、Stripeがそのエンポイ ントにリクエストを送信する。そのため、インターネットからアクセス可能な サーバーを準備するなどの作業が必要になる。または開発時はNgrokのような サービスを用いることもできる。

StripeではStripe CLIを用いることでインターネットからのアクセスを受け付 けるようなエンドポイントを準備することなく、Webhookの機能をテストを可 能にする機能を提供している。そのために stripe listen を用いる。

stripe listen --forward-to localhost:8000

stripe listen はStripeと通信し、発生したイベントを受け取る。そしてそ の受け取ったイベントを --forward-to で指定したところへ転送する。

                                +--------------------------------+
                                |                                |
                                | local                          |
                                |                                |
+----------------+              |   +----------------+           |
|                |              |   |                |           |
| Stripe         +<---------------->| Stripe CLI     |           |
|                |              |   |                |           |
-----------------+              |   ------+----------+           |
                                |         |                      |
                                |         | forward-toで指定     |
                                |         |                      |
                                |         |                      |
                                |         v                      |
                                |   +-----+----------+           |
                                |   |                |           |
                                |   | 開発用サーバー |           |
                                |   |                |           |
                                |   +----------------+           |
                                |                                |
                                +--------------------------------+

PythonでStripeのWebhookのハンドラを実装する

StripeのWebhookを受信する実装に言語的な制限はない。ここでは例として、 Pythonを用いてAPIを実装する。直接使用するライブラリは、stripeパッケー ジ以外は標準ライブラリを用いることにする。

incoming_webhook_handler.py::

from http.server import BaseHTTPRequestHandler, HTTPServer

import stripe

stripe_webhook_signing_secret = "DUMMY"


class StripeWebhookSimpleHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        body_size_str: str = self.headers.get("Content-Length", "10")
        if body_size_str.isdigit():
            body_size = int(body_size_str)
        else:
            body_size = 10

        payload = self.rfile.read(body_size)
        sig_header = self.headers["Stripe-Signature"]
        event = stripe.Webhook.construct_event(
            payload=payload,
            sig_header=sig_header,
            secret=stripe_webhook_signing_secret,
        )
        print(event)


server = HTTPServer(("localhost", 8000), StripeWebhookSimpleHandler)
server.serve_forever()

以下のように実行する。

incoming_webhook_handler.py

起動したらインターネットに公開するか、前述したStripe CLIの機能を用いてイベントを受信する。 もちろん実際にWebhookを受け取ることも可能だが、SSL/TLS対応はしていないため、その場合、サーバー証明書を用意し適切にSSL終端をする必要がある。

発生するイベント

発生するイベントは予め決められている3。 発生するイベントがどのような形式のデータを返してくるのかも決められている4。 また管理画面上からどのイベントを送信するかを制限できる。

Webhookでは拡張属性を利用できない

Webhookでは拡張属性(ドキュメントにexapandableと指定がある)は 用いることができない5。例えばcardオブ ジェクトのcustomer属性は拡張属性のため、cardに関連するイベントでは customerの情報(例えば顧客IDなど)を取得できない。取得するためには別途 APIを呼び出して取得する旨記述されていた。そのため必要な情報は予め保存 しておくケースが多いと考えられる。

StripeからのリクエストをIPアドレスで制限する

Stripeから送信されるWebhookは必ず特定のIPアドレスから送信され る6。Webhookを受け取る際はこのIPアド レスから来たリクエストかを検証する必要がある。

Stripe API

Stripeで決済やそれに関連する操作を行うにはいくつかの方法がある。 その中の一つにWeb APIを直接呼び出す方法がある。

カードトークンの発行

:STRIPE_API_ORIGIN := (getenv "STRIPE_API_ORIGIN")
:STRIPE_PUBLIC_KEY := (getenv "STRIPE_PUBLIC_KEY")
:STRIPE_SECRET_KEY := (getenv "STRIPE_SECRET_KEY")

:DUMMY_CREDIT_CARD_NUMBER := (getenv "DUMMY_CREDIT_CARD_NUMBER")
:DUMMY_CREDIT_CARD_EXP_MONTH := (getenv "DUMMY_CREDIT_CARD_EXP_MONTH")
:DUMMY_CREDIT_CARD_EXP_YEAR := (getenv "DUMMY_CREDIT_CARD_EXP_YEAR")
:DUMMY_CREDIT_CARD_CVC := (getenv "DUMMY_CREDIT_CARD_CVC")

POST :STRIPE_API_ORIGIN/v1/tokens

card[number]=:DUMMY_CREDIT_CARD_NUMBER&card[exp_month]=:DUMMY_CREDIT_CARD_EXP_MONTH&card[exp_year]=:DUMMY_CREDIT_CARD_EXP_YEAR&card[cvc]=:DUMMY_CREDIT_CARD_CVC&key=:STRIPE_PUBLIC_KEY

顧客の作成

:STRIPE_API_ORIGIN := (getenv "STRIPE_API_ORIGIN")
:STRIPE_SECRET_KEY := (getenv "STRIPE_SECRET_KEY")

POST :STRIPE_API_ORIGIN/v1/customers
Authorization: Bearer :STRIPE_SECRET_KEY

description="test2"

支払方法の登録

:STRIPE_API_ORIGIN := (getenv "STRIPE_API_ORIGIN")
:STRIPE_SECRET_KEY := (getenv "STRIPE_SECRET_KEY")

:DUMMY_CREDIT_CARD_NUMBER := (getenv "DUMMY_CREDIT_CARD_NUMBER")
:DUMMY_CREDIT_CARD_EXP_MONTH := (getenv "DUMMY_CREDIT_CARD_EXP_MONTH")
:DUMMY_CREDIT_CARD_EXP_YEAR := (getenv "DUMMY_CREDIT_CARD_EXP_YEAR")
:DUMMY_CREDIT_CARD_CVC := (getenv "DUMMY_CREDIT_CARD_CVC")

POST :STRIPE_API_ORIGIN/v1/payment_methods
Authorization: Bearer :STRIPE_SECRET_KEY

type=card&card[number]=:DUMMY_CREDIT_CARD_NUMBER&card[exp_month]=:DUMMY_CREDIT_CARD_EXP_MONTH&card[exp_year]=:DUMMY_CREDIT_CARD_EXP_YEAR&card[cvc]=:DUMMY_CREDIT_CARD_CVC

カードトークンを使用した支払方法の登録

:STRIPE_API_ORIGIN := (getenv "STRIPE_API_ORIGIN")
:STRIPE_SECRET_KEY := (getenv "STRIPE_SECRET_KEY")
:STRIPE_CARD_TOKEN := (getenv "STRIPE_CARD_TOKEN")

POST :STRIPE_API_ORIGIN/v1/payment_methods
Authorization: Bearer :STRIPE_SECRET_KEY

type=card&card[token]=:STRIPE_CARD_TOKEN

支払い方法のアタッチ

:STRIPE_API_ORIGIN := (getenv "STRIPE_API_ORIGIN")
:STRIPE_SECRET_KEY := (getenv "STRIPE_SECRET_KEY")
:STRIPE_PAYMENT_METHOD_ID := (getenv "STRIPE_PAYMENT_METHOD_ID")
:STRIPE_CUSTOMER_ID := (getenv "STRIPE_CUSTOMER_ID")

POST :STRIPE_API_ORIGIN/v1/payment_methods/:STRIPE_PAYMENT_METHOD_ID/attach
Authorization: Bearer :STRIPE_SECRET_KEY

customer=:STRIPE_CUSTOMER_ID

支払い情報を作成

:STRIPE_API_ORIGIN := (getenv "STRIPE_API_ORIGIN")
:STRIPE_SECRET_KEY := (getenv "STRIPE_SECRET_KEY")
:STRIPE_PAYMENT_METHOD_ID := (getenv "STRIPE_PAYMENT_METHOD_ID")
:STRIPE_CUSTOMER_ID := (getenv "STRIPE_CUSTOMER_ID")

POST :STRIPE_API_ORIGIN/v1/payment_intents
Authorization: Bearer :STRIPE_SECRET_KEY

confirmation_method=manual&amount=500&currency=jpy&payment_method_types[]=card&customer=:STRIPE_CUSTOMER_ID&payment_method=:STRIPE_PAYMENT_METHOD_ID

キャプチャー

:STRIPE_API_ORIGIN := (getenv "STRIPE_API_ORIGIN")
:STRIPE_SECRET_KEY := (getenv "STRIPE_SECRET_KEY")
:PAYMENT_INTENT_ID := (getenv "PAYMENT_INTENT_ID")
:PAYMENT_METHOD_ID := (getenv "PAYMENT_METHOD_ID")

POST :STRIPE_API_ORIGIN/v1/payment_intents/:PAYMENT_INTENT_ID/capture
Authorization: Bearer :STRIPE_SECRET_KEY

確認

:STRIPE_API_ORIGIN := (getenv "STRIPE_API_ORIGIN")
:STRIPE_SECRET_KEY := (getenv "STRIPE_SECRET_KEY")

:STRIPE_PAYMENT_INTENT_ID := (getenv "STRIPE_PAYMENT_INTENT_ID")
:STRIPE_PAYMENT_METHOD_ID := (getenv "STRIPE_PAYMENT_METHOD_ID")

POST :STRIPE_API_ORIGIN/v1/payment_intents/:STRIPE_PAYMENT_INTENT_ID/confirm
Authorization: Bearer :STRIPE_SECRET_KEY

payment_method=:STRIPE_PAYMENT_METHOD_ID

従量課金の量を設定する

:STRIPE_API_ORIGIN := (getenv "STRIPE_API_ORIGIN")
:STRIPE_SECRET_KEY := (getenv "STRIPE_SECRET_KEY")
:STRIPE_USAGE_RECORD_ID := (getenv "STRIPE_SECRET_KEY")
:TIMESTAMP = 1615283548

POST :STRIPE_API_ORIGIN/v1/subscription_items/:STRIPE_USAGE_RECORD_ID/usage_records
Authorization: Bearer :STRIPE_SECRET_KEY

quantity=1&timestamp=:TIMESTAMP

定期購読の作成

:STRIPE_API_ORIGIN := (getenv "STRIPE_API_ORIGIN")
:STRIPE_SECRET_KEY := (getenv "STRIPE_SECRET_KEY")
:STRIPE_CUSTOMER_ID := (getenv "STRIPE_CUSTOMER_ID")
:STRIPE_PRICE_ID := (getenv "STRIPE_PRICE_ID")

POST :STRIPE_API_ORIGIN/v1/subscriptions
Authorization: Bearer :STRIPE_SECRET_KEY

customer=:STRIPE_CUSTOMER_ID&items[0][price]=:STRIPE_PRICE_ID

定期購読の更新

:STRIPE_API_ORIGIN := (getenv "STRIPE_API_ORIGIN")
:STRIPE_SECRET_KEY := (getenv "STRIPE_SECRET_KEY")
:STRIPE_SUBSCRIPTION_ID := (getenv "STRIPE_SUBSCRIPTION_ID")
:STRIPE_PRICE_ID := (getenv "STRIPE_PRICE_ID")

POST :STRIPE_API_ORIGIN/v1/subscriptions/:STRIPE_SUBSCRIPTION_ID
Authorization: Bearer :STRIPE_SECRET_KEY

items[0][price]=:STRIPE_PRICE_ID

定期購読のキャンセル

:STRIPE_API_ORIGIN := (getenv "STRIPE_API_ORIGIN")
:STRIPE_SECRET_KEY := (getenv "STRIPE_SECRET_KEY")
:STRIPE_SUBSCRIPTION_ID := (getenv "STRIPE_SUBSCRIPTION_ID")

DELETE :STRIPE_API_ORIGIN/v1/subscriptions/:STRIPE_SUBSCRIPTION_ID
Authorization: Bearer :STRIPE_SECRET_KEY

納税番号

国にはそれぞれに納税者に対してユニークな番号を発行している。それらは納 税番号と呼ばれる。この納税番号をStripe上に登録し管理するためのAPIがあ る。=type= で許容される値はドキュメントに記載されてい る7

:STRIPE_API_ORIGIN =
:STRIPE_SECRET_KEY =
:STRIPE_CUSTOMER_ID =
:STRIPE_TAX_TYPE = jp_cn
:STRIPE_TAX_VALUE = 1234567890123

POST :STRIPE_API_ORIGIN/v1/customers/:STRIPE_CUSTOMER_ID/tax_ids
Authorization: Bearer :STRIPE_SECRET_KEY
Content-Type: application/x-www-form-urlencoded

type=:STRIPE_TAX_TYPE&value=:STRIPE_TAX_VALUE

プラン

プランは古いAPIであり、プライスにより置き換えられる。まだプランを使用 しているユーザーのために残されておりAPIも呼び出し可能だが、おそらくそ の内廃止になると思われる。

:STRIPE_API_ORIGIN := (getenv "STRIPE_API_ORIGIN")
:STRIPE_SECRET_KEY := (getenv "STRIPE_SECRET_KEY")
:PLAN_ID =

GET :STRIPE_API_ORIGIN/v1/plans/:PLAN_ID
Authorization: Bearer :STRIPE_SECRET_KEY

テスト用クレジットカード

テストで使用できるクレジットカードとして様々な状態のカード番号が用意されている。 https://stripe.com/docs/testing

クレジットカードのテストにはこれらを用いる。 当然だが本番環境では使用できない。

脚注