Stripe Checkoutでサクッと決済機能をサービスに導入する

2024/01/15に公開

Stripe Checkoutを使うと決済機能をお手軽にいれられる

Stripeは決済サービスとして人気が高いのはエンジニアフレンドリーだからだと思います。さまざまなレベルで組み込みをしやすくなっています。APIを使ってがっつり組み込んでもいいですし、今回紹介するように処理のほとんどをStripeにまかせることもできます。

https://stripe.com/jp/payments/checkout

決済系の機能をがっつりつくるのは手間ですし怖さもありますよね。Stripe Checkoutを使うと決済はStripeのページで行い、サブスクの管理もStripeで...というような実装が可能です。DBでいろいろ保持しておく必要もありません。

最近はこれが増えてきているようでOpenAIのChatGPTなんかもStripe Checkoutが使われているようです。今回はFastAPI(python)を使ってどんな感じで実装するのか、ポイントをまとめて紹介します。

開発環境をつくる

まずはStripeのアカウントをつくります。
https://stripe.com/jp/

ライブラリをインストール

pip install --upgrade stripe

.envにStripeの環境変数をセット

STRIPE_SECRET_KEY=xxx...
STRIPE_ENDPOINT_SECRET=xxx...

開発者ページから「シークレットキー」と「公開可能キー」を取得して設定しましょう。StripeのAPIを利用する際に使います。

なお、Stripeには本番環境とテスト環境が用意されており、テストモードにして実際に決済をテストしたりすることができます。便利。

Pythonだけじゃなくいろんな言語でライブラリがあるので、詳しくはこちらから開発環境をセットしてください。
https://stripe.com/docs/development/quickstart?lang=python

Stripe CLIをインストール

実際に決済やサブスクの更新、またはカード決済エラーなどあらゆるイベントがおこった際にWebHookを使ってイベントを受け取ることができます。そしてStripe CLIを使うことでローカルでテスト決済などを行い、実際にイベントをリアルタイムにキャッチすることができます。

homebrewの場合は以下をターミナルで実行。

brew install stripe/stripe-cli/stripe

ほかにも環境に合わせたインストール方法が用意されています。
https://stripe.com/docs/development/quickstart?lang=python

stripe login

でログインしたら

stripe listen --forward-to localhost:8000/v1/stripe/webhook

のようにすることでイベントをリッスンできる状態になります。localhost:8000…の部分は実際に動かしているローカルのURLをいれてください。そうするとイベントはすべてlocalhost:8000/v1/stripe/webhookに送られるようになります。

全体の流れ

全体はこのようになっており、基本的にはカスタマーを作成したらカスタマーIDを保存しておけば商品購入ページへ飛ばしたり、購入後はカード変更などはStripeのカスタマーポータルを使って変更してもらうようなことができます。

テストカード

テスト環境で使うカード番号などはこちらで確認できます。
https://stripe.com/docs/testing?locale=ja-JP

商品登録

あらかじめ商品を登録しておきましょう。作成後にprice_idを渡して決済させるのでそこだけコピーしておきます。(price_xxx...ではじまっているものです)

カスタマー作成

import stripe

stripe.api_key = os.environ.get("STRIPE_SECRET_KEY")
customer = stripe.Customer.create(
	name="任意のユーザー名", email="メールアドレス"
)

Stripeにてカスタマーが作成されます。

チェックアウトセッション作成

import stripe

stripe.api_key = os.environ.get("STRIPE_SECRET_KEY")
checkout_session = stripe.checkout.Session.create(
    line_items=[
	{
	    "price": PRICE_ID,
	    "quantity": 1,
	},
    ],
    mode="payment",
    customer=customer_id,
    success_url=YOUR_DOMAIN + "/app/?status=success",
    cancel_url=YOUR_DOMAIN + "/app/?status=cancel",
)

PRICE_IDにはStripeであらかじめ作成した商品のprice_idをいれます。success_urlは成功した時に返ってくるURL、キャンセルはキャンセルした場合のURLです。

checkout_session.urlに飛ばすとこのように決済ページに飛びます。

カスタマーポータルのURLを取得

import stripe

stripe.api_key = os.environ.get("STRIPE_SECRET_KEY")
session = stripe.billing_portal.Session.create(
    customer=customer_id,
    return_url="/app/",
)

return_urlはカスタマーポータルからもどってくるURLを設定できます。

session.urlに飛ぶとこのような画面がでます。ここでプランなど契約していればその情報が表示されるようになります。サービスでプランページを準備しなくていいということですね、楽ちんです。

Webhookでのイベント処理

ここはサービスによって処理方法が違いますが、雰囲気としてこのような感じでイベントをキャッチできます。
https://stripe.com/docs/webhooks?locale=ja-JP

import stripe
endpoint_secret = os.environ.get("STRIPE_ENDPOINT_SECRET")

@router.post("/webhook")
async def stripe_webhook(request: Request, db: Session = Depends(database.get_db)):
    payload = await request.body()
    sig_header = request.headers.get("stripe-signature")

    event = None

    try:
        event = stripe.Webhook.construct_event(payload, sig_header, endpoint_secret)
    except ValueError as e:
        # ペイロードが不正です
        raise HTTPException(status_code=400, detail="Invalid payload") from e
    except stripe.error.SignatureVerificationError as e:
        # 署名が不正です
        raise HTTPException(status_code=400, detail="Invalid signature") from e

    # サブスクリプションの支払いが成功したら
    if event["type"] == "invoice.payment_succeeded":
        invoice = event["data"]["object"]
	...

    # サブスクリプションがキャンセルされた場合、またはカードの支払いが失敗した場合
    elif (
        event["type"] == "customer.subscription.deleted"
        or event["type"] == "invoice.payment_failed"
    ):
        subscription = event["data"]["object"]
        ...

    # サブスクリプションの更新イベントを処理
    elif event["type"] == "customer.subscription.updated":
        subscription = event["data"]["object"]
        ...

    else:
        print("未処理のイベントタイプ {}".format(event["type"]))

    return JSONResponse(content={"success": True})

Stripe Checkoutを使うことで決済部分をStripeに丸投げすることができ、サービスの開発に集中することができるようになります。Stripeは柔軟に開発組み込みができ、カスタマイズにも柔軟に対応することができるのでおすすめです。

Discussion