Webhook 履約
Webhook 是 Stripe 主動 POST 到你伺服器的事件。顧客可能付完就關分頁,成功頁不是履約依據。官方文件:Receive Stripe events。
為什麼一定要驗證簽章
/webhook 若公開在網路上,攻擊者可以自己 POST 一份 checkout.session.completed。驗證 Stripe-Signature 才能確定請求來自 Stripe,且內容沒被改過。
驗證必須使用原始 request body(bytes)。先 request.get_json() 再驗證,簽章會失敗。
Flask 範例
import os
import stripe
from flask import Flask, request
app = Flask(__name__)
stripe.api_key = os.environ["STRIPE_SECRET_KEY"]
WEBHOOK_SECRET = os.environ["STRIPE_WEBHOOK_SECRET"]
processed = set() # 示範用;正式請存資料庫
@app.post("/webhook")
def webhook():
payload = request.get_data()
sig_header = request.headers.get("Stripe-Signature")
try:
event = stripe.Webhook.construct_event(
payload, sig_header, WEBHOOK_SECRET
)
except ValueError:
return "Invalid payload", 400
except stripe.error.SignatureVerificationError:
return "Invalid signature", 400
if event["id"] in processed:
return "", 200
processed.add(event["id"])
if event["type"] == "checkout.session.completed":
session = event["data"]["object"]
if session.get("payment_status") == "paid":
fulfill(session)
elif event["type"] == "payment_intent.succeeded":
intent = event["data"]["object"]
fulfill_intent(intent)
return "", 200
def fulfill(session):
sku = (session.get("metadata") or {}).get("sku")
# 標記訂單已付款、寄信、開通權限
print("fulfill", session["id"], sku)
def fulfill_intent(intent):
print("paid", intent["id"], intent["amount"])
處理成功後盡快回 200。邏輯太慢時,先記 event id 再背景處理,否則 Stripe 會重試。
要聽哪些事件
| 你用的整合 | 主要事件 |
|---|---|
| Checkout 一次付清 | checkout.session.completed(確認 payment_status=paid) |
| PaymentIntent | payment_intent.succeeded |
| 訂閱 | customer.subscription.created / updated / deleted、invoice.paid、invoice.payment_failed |
一次付清建議兩個都做:Checkout 完成、以及對應的 PaymentIntent 成功。以你的訂單狀態機做冪等,重複呼叫只更新一次。
本機測試
把印出的 whsec_... 設成 STRIPE_WEBHOOK_SECRET。另開終端:
CLI 密鑰只給本機轉送用。上線後在 Dashboard → Webhooks 新增 https://你的網域/webhook,會得到另一把 whsec_,放到正式環境變數。
常見錯誤
- 用 JSON parser 吃掉 body 再驗證 → 簽章失敗
- 本機
whsec拿到正式機、或 Dashboard 密鑰拿到 CLI → 同樣失敗 - 收到事件就出貨、沒看
payment_status→ 未付款 Session 也可能有 completed 以外的狀態組合,一次付清請確認paid - 沒做 event id 去重 → 顧客收到兩次商品