Stripe連携をスムーズに:開発者向けコードサンプル

開発者の皆さん、こんにちは!もしアプリに決済機能を追加する必要があると感じたことがあるなら、Stripeが候補に挙がったことは一度や二度ではないでしょう。Stripeは本当に素晴らしいツールですが、全てを完璧に設定するのは、まるでタコと格闘するようなものです。動く部分がたくさんあって、次にどこを掴めばいいのか分からなくなることがありますよね。

そこで、このガイドの出番です。私は公式ドキュメントをただ繰り返すつもりはありません(それらも素晴らしいですが!)。私の目的は、Stripeを統合するための明確で手間のかからない道筋を示すことです。そして、いくつかの人気のある言語で、実用的なコードスニペットも完全にご用意しました。これは、経験豊富な友人があなたのために頭痛の種を減らしたいと思っている、そんな親しみやすいおしゃべりだと思ってください。

なぜStripeなのか?(そして、それを正しく導入することの重要性)

コードを掘り下げる前に、なぜStripeが多くの人にとって「頼みの綱」なのかを簡単に説明しましょう。堅牢で、安全で、素晴らしい開発者ツールが揃っています。しかし、強力なツールを持っているだけでは十分ではありません。それを効果的に使いこなす必要があります。適切に統合された決済システムは、単にお金を受け取るだけではありません。それは、ユーザーにスムーズで信頼性の高い体験を提供し、ビジネスにおける潜在的な摩擦点を減らすことにつながります。

実践編:Stripe環境のセットアップ

まず最初に、Stripeアカウントが必要です。stripe.com にアクセスして登録しましょう。ログインすると、開発者ダッシュボードにAPIキーが見つかります。これには2つのセットがあります。「公開可能キー」(フロントエンド用で、共有しても安全です)と「シークレットキー」(バックエンド用で、これは秘密にしておきましょう!)です。

ローカル開発の場合、シークレットキーを保存するために.envファイルをセットアップすることをおすすめします。決してハードコードしないでください!

.envファイルの例:

STRIPE_SECRET_KEY=sk_test_YOUR_SECRET_KEY
STRIPE_PUBLISHABLE_KEY=pk_test_YOUR_PUBLISHABLE_KEY

基本的なCheckout Sessionの作成

最近、Stripeで決済を処理する最も一般的な方法は、Checkout Sessionを利用することです。これにより、PCIコンプライアンスやユーザーインターフェースのスタイリングに関する多くの負担がStripe側にオフロードされます。これは我々開発者にとって大きなメリットです。

フロントエンドは単にユーザーをStripeがホストするページにリダイレクトし、決済が完了すると、ユーザーはあなたのサイトに戻されます。

バックエンドからチェックアウトセッションを開始する方法は以下の通りです。

Node.jsの例(expressを使用):

// backend/server.js
const express = require('express');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const app = express();

app.use(express.json()); // JSONのパース用

app.post('/create-checkout-session', async (req, res) => {
  try {
    const session = await stripe.checkout.sessions.create({
      payment_method_types: ['card'],
      line_items: [
        {
          price_data: {
            currency: 'usd',
            product_data: {
              name: 'Awesome Product',
            },
            unit_amount: 2000, // $20.00
          },
          quantity: 1,
        },
      ],
      mode: 'payment',
      success_url: 'http://localhost:3000/success?session_id={CHECKOUT_SESSION_ID}',
      cancel_url: 'http://localhost:3000/cancel',
    });
    res.json({ id: session.id });
  } catch (error) {
    console.error('Error creating checkout session:', error);
    res.status(500).json({ error: error.message });
  }
});

app.listen(4242, () => console.log('Node server listening on port 4242!'));

Pythonの例(Flaskを使用):

# backend/app.py
from flask import Flask, jsonify, request, redirect, url_for
import stripe
import os

app = Flask(__name__)

stripe.api_key = os.getenv('STRIPE_SECRET_KEY')

@app.route('/create-checkout-session', methods=['POST'])
def create_checkout_session():
    try:
        session = stripe.checkout.Session.create(
            payment_method_types=['card'],
            line_items=[
                {
                    'price_data': {
                        'currency': 'usd',
                        'product_data': {
                            'name': 'Awesome Product',
                        },
                        'unit_amount': 2000,
                    },
                    'quantity': 1,
                }
            ],
            mode='payment',
            success_url='http://localhost:3000/success?session_id={CHECKOUT_SESSION_ID}',
            cancel_url='http://localhost:3000/cancel',
        )
        return jsonify(id=session.id)
    except Exception as e:
        print(f'Error creating checkout session: {e}')
        return jsonify(error=str(e)), 500

if __name__ == '__main__':
    app.run(port=4242)

フロントエンドでは、このエンドポイントを呼び出した後、ユーザーをリダイレクトします。

フロントエンドの例(Stripe.jsを使用):

<!-- frontend/index.html -->
<!DOCTYPE html>
<html>
<head>
  <title>素晴らしい商品を購買する</title>
  <script src="https://js.stripe.com/v3/"></script>
</head>
<body>
  <h1>素晴らしい商品を購買する</h1>
  <button id="checkout-button">購入手続きへ</button>

  <script type="text/javascript">
    const stripe = Stripe('pk_test_YOUR_PUBLISHABLE_KEY'); // あなたの公開キーに置き換えてください

    const checkoutButton = document.getElementById('checkout-button');

    checkoutButton.addEventListener('click', async () => {
      try {
        const response = await fetch('/create-checkout-session', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
        });
        const session = await response.json();

        if (session.id) {
          const result = await stripe.redirectToCheckout({
            sessionId: session.id,
          });

          if (result.error) {
            alert(result.error.message);
          }
        } else if (session.error) {
            alert(session.error.message);
        }

      } catch (error) {
        console.error('チェックアウトの開始中にエラーが発生しました:', error);
        alert('チェックアウトを開始できませんでした。もう一度お試しください。');
      }
    });
  </script>
</body>
</html>

決済後の処理:Webhooksはあなたの味方です

決済が成功した後、バックエンドはどうやって「何が起こったか」を知るのでしょうか?ユーザーがブラウザを閉じる可能性があるので、success_urlだけに頼ることはできません。その答えはWebhooksです。

Stripeは、何か重要な出来事(決済の成功、返金、サブスクリプションの更新など)が起こるたびに、指定されたWebhookエンドポイントにイベントを送信します。あなたのサーバーはこれらのイベントを待ち受け、それに応じて動作します。

Webhookエンドポイントの設定

まず、Stripeがイベントを送信するための公開URLが必要です。ローカル開発では、ngrokのようなツールが非常に役立ちます。これらは公開URLからあなたのローカルマシンへの安全なトンネルを作成します。ngrokのドキュメントをチェックしてセットアップしましょう。

ngrokが動作したら、通常は ngrok http 4242 のように起動します(あなたのサーバーがポート4242で動作している場合)。すると公開URL(例: https://your-random-subdomain.ngrok-free.app)が提供されます。

次に、Stripeダッシュボードで「開発者」>「Webhooks」に進み、「エンドポイントを追加」をクリックします。あなたのngrokのURL(例: https://your-random-subdomain.ngrok-free.app/webhook)を貼り付け、リッスンしたいイベント(checkout.session.completedは決済の場合の良い開始点です)を選択します。

Webhook署名の検証

Webhook署名を検証することは絶対に不可欠です。これにより、イベントが本当にStripeから送信されたものであり、改ざんされていないことが保証されます。

StripeはStripe-Signatureヘッダーに署名を含めます。これを検証するには、あなたのWebhookシークレット(StripeダッシュボードのWebhookエンドポイント詳細で確認できます)が必要です。

Node.js Webhookの例:

// backend/server.js (既存のserver.jsに追加)

// 重要: このエンドポイントは特定のものであり、一般的なJSONパースの前に来るようにしてください。
// body-parserやexpress.jsonをグローバルに使用している場合、Webhookは生のボディを必要とします。

app.post('/webhook', express.raw({type: 'application/json'}), async (req, res) => {
  const sig = req.headers['stripe-signature'];
  let event;
  const endpointSecret = process.env.STRIPE_WEBHOOK_SECRET; // StripeのWebhook設定から取得

  try {
    event = stripe.webhooks.constructEvent(req.body, sig, endpointSecret);
  } catch (err) {
    console.error(`Webhook Error: ${err.message}`);
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  // イベントを処理
  switch (event.type) {
    case 'checkout.session.completed':
      const checkoutSession = event.data.object;
      // 購入を完了させる、データベースを更新する、確認メールを送信するなど
      console.log(`Checkout session completed for: ${checkoutSession.id}`);
      // checkoutSessionオブジェクトから顧客情報、品目などにアクセス
      break;
    // ... その他のイベントタイプを処理
    default:
      console.log(`未処理のイベントタイプ ${event.type}`);
  }

  // イベントの受信を確認するために200レスポンスを返す
  res.send();
});

Python Webhookの例:

# backend/app.py (既存のapp.pyに追加)

@app.route('/webhook', methods=['POST'])
def webhook_received():
    request_data = request.data.decode('utf-8')
    signature = request.headers.get('stripe-signature')
    webhook_secret = os.getenv('STRIPE_WEBHOOK_SECRET') # StripeのWebhook設定から取得

    try:
        event = stripe.Webhook.construct_event(
            payload=request_data,
            sig_header=signature,
            secret=webhook_secret
        )
    except ValueError as e:
        # 無効なペイロード
        print(f'無効なペイロード: {e}')
        return jsonify({'error': 'Invalid payload'}), 400
    except stripe.error.SignatureVerificationError as e:
        # 無効な署名
        print(f'無効な署名: {e}')
        return jsonify({'error': 'Invalid signature'}), 400

    # イベントを処理
    if event['type'] == 'checkout.session.completed':
        checkout_session = event['data']['object']
        # 購入を完了させる、データベースを更新する、確認メールを送信するなど
        print(f'Checkout session completed for: {checkout_session.id}')
    # ... その他のイベントタイプを処理

    return jsonify({'status': 'success'}), 200

押さえておきたいクイックなベストプラクティス

  • エラーハンドリングが鍵: Stripe APIの呼び出しは、常にtry...catchブロックで囲んでください。決済は非常に重要なので、何か問題が発生した場合でも適切に処理できるようにしたいものです。
  • 冪等性(Idempotency): API呼び出しを行う際(特に決済などリソースを作成する呼び出し)、冪等性キーを使用してください。これにより、リクエストが誤って複数回送信された場合(例:ネットワークの障害によるもの)、Stripeがそれを一度だけ処理することが保証されます。ほとんどのクライアントライブラリはこれを自動で処理してくれますが、知っておくことは良いことです。
  • テストモード vs. ライブモード: 常にtestモードで徹底的に開発とテストを行ってください。Stripeが提供するテストカード番号を使用しましょう。実際の取引を行う準備が完全に整ってからのみ、liveモードのシークレットに切り替えてください。
  • HTTPSは必須: 本番アプリケーションでは、常にドメインでHTTPSを使用していることを確認してください。これは機密性の高い決済情報を保護します。
  • 必要なものだけを保存: 機密性の高いカード情報をサーバーに保存することは避けてください。Stripeに任せましょう。顧客や決済方法を参照する必要がある場合は、Stripeの顧客IDや決済方法IDを使用してください。

まとめ

Stripeは非常に強力なプラットフォームであり、その統合は途方もないタスクである必要はありません。キーの設定、チェックアウトセッションの作成、そして何よりもWebhookの処理といった、管理しやすいステップに分解することで、決済フローをかなりスムーズに動作させることができます。

これはあくまで出発点に過ぎません。Stripeは、サブスクリプション、請求書発行、接続アカウント、さらに複雑な決済フローなど、はるかに多くの機能を提供しています。しかし、これらの基礎となる部分が整っていれば、あなたのアプリケーションに堅牢で信頼性の高い決済処理を組み込む準備は万端です。ハッピーコーディング!

この記事を共有