10_webhooks.py
stripe_demo/examples/10_webhooks.py
"""
Stripe Webhooks – receive and verify events from Stripe.
Use STRIPE_WEBHOOK_SECRET (from Stripe CLI or Dashboard) to verify signatures.
Docs: https://docs.stripe.com/webhooks
"""
import stripe
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from config import STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, ensure_stripe_key
stripe.api_key = STRIPE_SECRET_KEY
def verify_and_parse_event(payload: bytes, sig_header: str, webhook_secret: str = None):
"""
Verify webhook signature and parse event. Use in your HTTP handler.
payload: raw request body (bytes)
sig_header: request.headers.get('Stripe-Signature')
webhook_secret: endpoint signing secret (whsec_...). Default: STRIPE_WEBHOOK_SECRET.
"""
secret = webhook_secret or STRIPE_WEBHOOK_SECRET
if not secret:
raise ValueError("STRIPE_WEBHOOK_SECRET or webhook_secret required for verification")
try:
event = stripe.Webhook.construct_event(payload, sig_header, secret)
except ValueError as e:
raise ValueError(f"Invalid payload: {e}")
except stripe.error.SignatureVerificationError as e:
raise stripe.error.SignatureVerificationError(f"Invalid signature: {e}")
return event
def handle_event(event):
"""
Example handler: branch on event.type and process event.data.object.
Return quickly (e.g. 200) then do heavy work in background (queue, celery, etc.).
"""
obj = event.data.object
if event.type == "payment_intent.succeeded":
print(f"PaymentIntent succeeded: {obj.id}")
# e.g. fulfill order, update DB
elif event.type == "payment_intent.payment_failed":
print(f"PaymentIntent failed: {obj.id}")
elif event.type == "checkout.session.completed":
print(f"Checkout completed: {obj.id} customer={obj.customer}")
# session.payment_status, session.subscription, session.mode
elif event.type == "customer.subscription.created":
print(f"Subscription created: {obj.id}")
elif event.type == "customer.subscription.updated":
print(f"Subscription updated: {obj.id}")
elif event.type == "customer.subscription.deleted":
print(f"Subscription canceled: {obj.id}")
elif event.type == "invoice.paid":
print(f"Invoice paid: {obj.id}")
elif event.type == "invoice.payment_failed":
print(f"Invoice payment failed: {obj.id}")
else:
print(f"Unhandled event type: {event.type}")
# --- Flask example (uncomment and use with: flask run) ---
# from flask import Flask, request, jsonify
# app = Flask(__name__)
#
# @app.route("/webhook", methods=["POST"])
# def webhook():
# payload = request.get_data()
# sig_header = request.headers.get("Stripe-Signature")
# try:
# event = verify_and_parse_event(payload, sig_header)
# except (ValueError, stripe.error.SignatureVerificationError) as e:
# return jsonify({"error": str(e)}), 400
# handle_event(event)
# return jsonify({"received": True}), 200
# --- Minimal HTTP server example (for local testing with stripe listen) ---
def run_simple_webhook_server(port: int = 4242):
"""Run a minimal POST /webhook server. Use with: stripe listen --forward-to localhost:4242/webhook"""
try:
from http.server import HTTPServer, BaseHTTPRequestHandler
except ImportError:
print("Use Flask or another framework for a real webhook server.")
return
class Handler(BaseHTTPRequestHandler):
def do_POST(self):
if self.path != "/webhook":
self.send_response(404)
self.end_headers()
return
length = int(self.headers.get("Content-Length", 0))
payload = self.rfile.read(length)
sig = self.headers.get("Stripe-Signature", "")
try:
event = verify_and_parse_event(payload, sig)
handle_event(event)
except Exception as e:
self.send_response(400)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(b'{"error":"' + str(e).encode() + b'"}')
return
self.send_response(200)
self.end_headers()
def log_message(self, format, *args):
print(f"[webhook] {format % args}")
server = HTTPServer(("", port), Handler)
print(f"Serving /webhook on port {port}. Run: stripe listen --forward-to localhost:{port}/webhook")
server.serve_forever()
if __name__ == "__main__":
print("Webhook verification and handling helpers.")
print("To test locally: stripe listen --forward-to localhost:4242/webhook")
print("Then run a Flask/app server that calls verify_and_parse_event + handle_event in POST /webhook.")
相關文章
config.py
config.py — python source code from the stripe demo learning materials (stripe_demo/config.py).
閱讀文章 →00_error_handling.py
00_error_handling.py — python source code from the stripe demo learning materials (stripe_demo/examples/00_error_handling.py).
閱讀文章 →01_customers.py
01_customers.py — python source code from the stripe demo learning materials (stripe_demo/examples/01_customers.py).
閱讀文章 →02_products_prices.py
02_products_prices.py — python source code from the stripe demo learning materials (stripe_demo/examples/02_products_prices.py).
閱讀文章 →03_payment_intents.py
03_payment_intents.py — python source code from the stripe demo learning materials (stripe_demo/examples/03_payment_intents.py).
閱讀文章 →04_checkout_sessions.py
04_checkout_sessions.py — python source code from the stripe demo learning materials (stripe_demo/examples/04_checkout_sessions.py).
閱讀文章 →