Skip to content

建立 2026-09-14 更新 2026-09-14

PaymentIntent 自訂表單

當結帳必須留在你的網域、版面要完全自訂時,使用 PaymentIntent 加上前端的 Payment Element。這是本 repo server.py + public/ 的做法。

官方文件:Accept a payment。官方逐步影片(Python)已嵌在 官方影音

和 Checkout 差在哪

Checkout:伺服器建立 Session,瀏覽器離開(或嵌入整頁 Checkout)。

PaymentIntent:伺服器只建立「要收多少錢」的 Intent,瀏覽器留在你的 HTML,由 Stripe.js 畫卡號欄位並呼叫 confirmPayment

你要自己處理:載入 Stripe.js、錯誤訊息、3DS 導回、以及同樣不可少的 webhook。

後端:建立 PaymentIntent

金額仍在伺服器算。不要用前端傳來的 amount

import json
import os
import stripe
from flask import Flask, jsonify, render_template, request

app = Flask(__name__, static_folder="public", static_url_path="", template_folder="public")
stripe.api_key = os.environ["STRIPE_SECRET_KEY"]

def calculate_order_amount(items):
    # 以伺服器價格為準;這裡示範固定 NT$140(若改 USD 則是 1400 分)
    return 1400

@app.get("/checkout")
def checkout():
    return render_template("checkout.html")

@app.post("/create-payment-intent")
def create_payment():
    data = json.loads(request.data)
    intent = stripe.PaymentIntent.create(
        amount=calculate_order_amount(data.get("items", [])),
        currency="usd",
        automatic_payment_methods={"enabled": True},
        metadata={"sku": "xl-tshirt"},
    )
    return jsonify({"clientSecret": intent.client_secret})

client_secret 可以給前端,它只能確認這一筆 Intent,不能列出金鑰或改金額。Secret API key 仍然只能留在伺服器。

automatic_payment_methods 讓 Dashboard 勾選的付款方式自動出現在表單,不必改程式。

前端:掛上 Payment Element

checkout.html 需要 Stripe.js 與一個空的掛載點:

<script src="https://js.stripe.com/v3/"></script>
<form id="payment-form">
  <div id="payment-element"></div>
  <button id="submit">Pay now</button>
  <div id="payment-message"></div>
</form>

checkout.js 的核心步驟:

const stripe = Stripe("pk_test_你的公開金鑰");

const response = await fetch("/create-payment-intent", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ items: [{ id: "xl-tshirt" }] }),
});
const { clientSecret } = await response.json();

const elements = stripe.elements({ clientSecret });
elements.create("payment").mount("#payment-element");

const { error } = await stripe.confirmPayment({
  elements,
  confirmParams: {
    return_url: "http://localhost:4242/checkout",
  },
});

confirmPayment 會把卡號送到 Stripe。若需要 3D Secure,Stripe 會導向銀行再回到 return_url。頁面可用 query 裡的 payment_intent_client_secret 呼叫 retrievePaymentIntent 顯示結果。

完整檔案在 repo 的 public/checkout.htmlpublic/checkout.jspublic/checkout.css。逐步對照見 本 repo 範例

務必接 webhook

Intent 變成 succeeded 時,要聽 payment_intent.succeeded。不要只靠 return_url。見 Webhook 履約

什麼時候不要用這條路

賣單一商品、沒有複雜結帳 UI,用 Checkout 比較省事,稅與優惠券也比較完整。PaymentIntent 適合已經有自己的購物車 UI、必須高度客製的產品。