stripe-integration について
stripe-integration は、Web・モバイルアプリ開発者向けの Stripe 決済実装スキルです。チェックアウト、サブスクリプション、顧客管理、返金、Webhook、決済テストを扱います。
主な機能
- 01ホスト型チェックアウトと Payment Intents の実装
- 02定期サブスクリプションの設定
- 03顧客と支払い方法の管理
- 04Webhook の処理と検証
- 05返金処理と決済テスト
活用シーン
- 01アプリへの単発チェックアウト追加
- 02サブスクリプション課金の構築
- 03返金と異議申し立ての処理
- 04Webhook による決済イベント処理
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ ovachiever/ droid-tings --skill stripe-integrationインストール後、Claude Code で/stripe-integrationを入力して読み込まれたか確認します。
詳細
Stripe 連携
チェックアウト、サブスクリプション、Webhook、返金など、堅牢で PCI 準拠の決済フローを実現するための Stripe 決済処理連携を習得します。
このスキルを使用する場面
- Web/モバイルアプリに決済処理を実装する場合
- サブスクリプション課金システムを設定する場合
- 1 回限りの支払いや継続課金を処理する場合
- 返金や異議申し立てを処理する場合
- 顧客の支払い方法を管理する場合
- 欧州向け決済に SCA(強力な顧客認証)を実装する場合
- Stripe Connect を使用したマーケットプレイスの決済フローを構築する場合
主な概念
1. 決済フロー
Checkout Session(ホスト型)
- Stripe がホストする決済ページ
- PCI 準拠に関する負担を最小限に抑えられます
- 最も迅速に実装できます
- 1 回限りの支払いと継続課金に対応します
Payment Intents(カスタム UI)
- 決済 UI を完全に制御できます
- PCI 準拠のために Stripe.js が必要です
- 実装はより複雑です
- カスタマイズの選択肢が広がります
Setup Intents(支払い方法の保存)
- 課金せずに支払い方法を収集します
- サブスクリプションや将来の支払いに使用します
- 顧客による確認が必要です
2. Webhook
重要なイベント:
payment_intent.succeeded:支払いが完了しましたpayment_intent.payment_failed:支払いに失敗しましたcustomer.subscription.updated:サブスクリプションが変更されましたcustomer.subscription.deleted:サブスクリプションがキャンセルされましたcharge.refunded:返金が処理されましたinvoice.payment_succeeded:サブスクリプションの支払いが成功しました
3. サブスクリプション
構成要素:
- Product:販売するもの
- Price:金額と請求頻度
- Subscription:顧客の継続的な支払い
- Invoice:各請求サイクルで生成される請求書
4. 顧客管理
- 顧客レコードを作成・管理します
- 複数の支払い方法を保存します
- 顧客のメタデータを追跡します
- 請求情報を管理します
クイックスタート
import stripe
stripe.api_key = "sk_test_..."
# Create a checkout session
session = stripe.checkout.Session.create(
payment_method_types=['card'],
line_items=[{
'price_data': {
'currency': 'usd',
'product_data': {
'name': 'Premium Subscription',
},
'unit_amount': 2000, # $20.00
'recurring': {
'interval': 'month',
},
},
'quantity': 1,
}],
mode='subscription',
success_url='https://yourdomain.com/success?session_id={CHECKOUT_SESSION_ID}',
cancel_url='https://yourdomain.com/cancel',
)
# Redirect user to session.url
print(session.url)
決済の実装パターン
パターン 1:1 回限りの支払い(ホスト型チェックアウト)
def create_checkout_session(amount, currency='usd'):
"""Create a one-time payment checkout session."""
try:
session = stripe.checkout.Session.create(
payment_method_types=['card'],
line_items=[{
'price_data': {
'currency': currency,
'product_data': {
'name': 'Purchase',
'images': ['https://example.com/product.jpg'],
},
'unit_amount': amount, # Amount in cents
},
'quantity': 1,
}],
mode='payment',
success_url='https://yourdomain.com/success?session_id={CHECKOUT_SESSION_ID}',
cancel_url='https://yourdomain.com/cancel',
metadata={
'order_id': 'order_123',
'user_id': 'user_456'
}
)
return session
except stripe.error.StripeError as e:
# Handle error
print(f"Stripe error: {e.user_message}")
raise
パターン 2:カスタム Payment Intent フロー
def create_payment_intent(amount, currency='usd', customer_id=None):
"""Create a payment intent for custom checkout UI."""
intent = stripe.PaymentIntent.create(
amount=amount,
currency=currency,
customer=customer_id,
automatic_payment_methods={
'enabled': True,
},
metadata={
'integration_check': 'accept_a_payment'
}
)
return intent.client_secret # Send to frontend
# Frontend (JavaScript)
"""
const stripe = Stripe('pk_test_...');
const elements = stripe.elements();
const cardElement = elements.create('card');
cardElement.mount('#card-element');
const {error, paymentIntent} = await stripe.confirmCardPayment(
clientSecret,
{
payment_method: {
card: cardElement,
billing_details: {
name: 'Customer Name'
}
}
}
);
if (error) {
// Handle error
} else if (paymentIntent.status === 'succeeded') {
// Payment successful
}
"""
パターン 3:サブスクリプションの作成
def create_subscription(customer_id, price_id):
"""Create a subscription for a customer."""
try:
subscription = stripe.Subscription.create(
customer=customer_id,
items=[{'price': price_id}],
payment_behavior='default_incomplete',
payment_settings={'save_default_payment_method': 'on_subscription'},
expand=['latest_invoice.payment_intent'],
)
return {
'subscription_id': subscription.id,
'client_secret': subscription.latest_invoice.payment_intent.client_secret
}
except stripe.error.StripeError as e:
print(f"Subscription creation failed: {e}")
raise
パターン 4:顧客ポータル
def create_customer_portal_session(customer_id):
"""Create a portal session for customers to manage subscriptions."""
session = stripe.billing_portal.Session.create(
customer=customer_id,
return_url='https://yourdomain.com/account',
)
return session.url # Redirect customer here
Webhook の処理
セキュアな Webhook エンドポイント
from flask import Flask, request
import stripe
app = Flask(__name__)
endpoint_secret = 'whsec_...'
@app.route('/webhook', methods=['POST'])
def webhook():
payload = request.data
sig_header = request.headers.get('Stripe-Signature')
try:
event = stripe.Webhook.construct_event(
payload, sig_header, endpoint_secret
)
except ValueError:
# Invalid payload
return 'Invalid payload', 400
except stripe.error.SignatureVerificationError:
# Invalid signature
return 'Invalid signature', 400
# Handle the event
if event['type'] == 'payment_intent.succeeded':
payment_intent = event['data']['object']
handle_successful_payment(payment_intent)
elif event['type'] == 'payment_intent.payment_failed':
payment_intent = event['data']['object']
handle_failed_payment(payment_intent)
elif event['type'] == 'customer.subscription.deleted':
subscription = event['data']['object']
handle_subscription_canceled(subscription)
return 'Success', 200
def handle_successful_payment(payment_intent):
"""Process successful payment."""
customer_id = payment_intent.get('customer')
amount = payment_intent['amount']
metadata = payment_intent.get('metadata', {})
# Update your database
# Send confirmation email
# Fulfill order
print(f"Payment succeeded: {payment_intent['id']}")
def handle_failed_payment(payment_intent):
"""Handle failed payment."""
error = payment_intent.get('last_payment_error', {})
print(f"Payment failed: {error.get('message')}")
# Notify customer
# Update order status
def handle_subscription_canceled(subscription):
"""Handle subscription cancellation."""
customer_id = subscription['customer']
# Update user access
# Send cancellation email
print(f"Subscription canceled: {subscription['id']}")
Webhook のベストプラクティス
import hashlib
import hmac
def verify_webhook_signature(payload, signature, secret):
"""Manually verify webhook signature."""
expected_sig = hmac.new(
secret.encode('utf-8'),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected_sig)
def handle_webhook_idempotently(event_id, handler):
"""Ensure webhook is processed exactly once."""
# Check if event already processed
if is_event_processed(event_id):
return
# Process event
try:
handler()
mark_event_processed(event_id)
except Exception as e:
log_error(e)
# Stripe will retry failed webhooks
raise
顧客管理
def create_customer(email, name, payment_method_id=None):
"""Create a Stripe customer."""
customer = stripe.Customer.create(
email=email,
name=name,
payment_method=payment_method_id,
invoice_settings={
'default_payment_method': payment_method_id
} if payment_method_id else None,
metadata={
'user_id': '12345'
}
)
return customer
def attach_payment_method(customer_id, payment_method_id):
"""Attach a payment method to a customer."""
stripe.PaymentMethod.attach(
payment_method_id,
customer=customer_id
)
# Set as default
stripe.Customer.modify(
customer_id,
invoice_settings={
'default_payment_method': payment_method_id
}
)
def list_customer_payment_methods(customer_id):
"""List all payment methods for a customer."""
payment_methods = stripe.PaymentMethod.list(
customer=customer_id,
type='card'
)
return payment_methods.data
返金処理
def create_refund(payment_intent_id, amount=None, reason=None):
"""Create a refund."""
refund_params = {
'payment_intent': payment_intent_id
}
if amount:
refund_params['amount'] = amount # Partial refund
if reason:
refund_params['reason'] = reason # 'duplicate', 'fraudulent', 'requested_by_customer'
refund = stripe.Refund.create(**refund_params)
return refund
def handle_dispute(charge_id, evidence):
"""Update dispute with evidence."""
stripe.Dispute.modify(
charge_id,
evidence={
'customer_name': evidence.get('customer_name'),
'customer_email_address': evidence.get('customer_email'),
'shipping_documentation': evidence.get('shipping_proof'),
'customer_communication': evidence.get('communication'),
}
)
テスト
# Use test mode keys
stripe.api_key = "sk_test_..."
# Test card numbers
TEST_CARDS = {
'success': '4242424242424242',
'declined': '4000000000000002',
'3d_secure': '4000002500003155',
'insufficient_funds': '4000000000009995'
}
def test_payment_flow():
"""Test complete payment flow."""
# Create test customer
customer = stripe.Customer.create(
email="[email protected]"
)
# Create payment intent
intent = stripe.PaymentIntent.create(
amount=1000,
currency='usd',
customer=customer.id,
payment_method_types=['card']
)
# Confirm with test card
confirmed = stripe.PaymentIntent.confirm(
intent.id,
payment_method='pm_card_visa' # Test payment method
)
assert confirmed.status == 'succeeded'
リソース
- references/checkout-flows.md:チェックアウト実装の詳細
- references/webhook-handling.md:Webhook のセキュリティと処理
- references/subscription-management.md:サブスクリプションのライフサイクル
- references/customer-management.md:顧客と支払い方法の処理
- references/invoice-generation.md:請求書の生成と請求処理
- assets/stripe-client.py:本番環境対応の Stripe クライアントラッパー
- assets/webhook-handler.py:完全な Webhook プロセッサー
- assets/checkout-config.json:チェックアウト設定テンプレート
ベストプラクティス
- 必ず Webhook を使用する:クライアント側の確認だけに頼らないでください
- 冪等性:Webhook イベントを冪等に処理してください
- エラー処理:すべての Stripe エラーを適切に処理してください
- テストモード:本番環境に移行する前に、テストキーで十分にテストしてください
- メタデータ:メタデータを使用して、Stripe オブジェクトとデータベースを関連付けてください
- 監視:支払いの成功率とエラーを追跡してください
- PCI 準拠:サーバーでカードの生データを決して扱わないでください
- SCA 対応:欧州向け決済では 3D セキュアを実装してください
よくある落とし穴
- Webhook を検証しない:必ず Webhook 署名を検証してください
- Webhook イベントの不足:関連するすべての Webhook イベントを処理してください
- 金額のハードコード:セントなど、通貨の最小単位を使用してください
- 再試行ロジックがない:API 呼び出しに再試行処理を実装してください
- テストモードを軽視する:テストカードを使って、あらゆるエッジケースをテストしてください
コメント
Markdown 対応