工藤  晃

工藤 晃

1659960360

如何Flask Stripe 訂閱”

本教程介紹如何使用 Flask 和Stripe設置和收取每月定期訂閱付款。

條紋訂閱付款選項

有多種方法可以實現和處理 Stripe 訂閱,但最著名的兩種是:

  1. 固定價格訂閱
  2. 未來付款

在這兩種情況下,您都可以使用Stripe Checkout(Stripe 託管的結帳頁面)或Stripe Elements(一組用於構建支付表單的自定義 UI 組件)。如果您不介意將用戶重定向到 Stripe 託管的頁面並希望 Stripe 為您處理大部分付款流程(例如,創建客戶和付款意圖等),請使用 Stripe Checkout,否則請使用 Stripe Elements。

固定價格方法更容易設置,但您無法完全控制計費周期和付款。通過使用這種方法,Stripe 將在成功結賬後自動開始向您的客戶收取每個計費周期的費用。

固定價格步驟:

  1. 將用戶重定向到 Stripe Checkout(使用mode=subscription
  2. 創建一個監聽的 webhookcheckout.session.completed
  3. 調用 webhook 後,將相關數據保存到您的數據庫中

未來的付款方式更難設置,但這種方式讓您可以完全控制訂閱。您提前收集客戶詳細信息和付款信息,並在未來某個日期向您的客戶收費。這種方法還允許您同步計費周期,以便您可以在同一天向所有客戶收費。

未來的付款步驟:

  1. 將用戶重定向到 Stripe Checkout(帶有mode=setup)以收集付款信息
  2. 創建一個監聽的 webhookcheckout.session.completed
  3. 調用 webhook 後,將相關數據保存到您的數據庫中
  4. 從那裡,您可以使用Payment Intents API在未來的某個日期向付款方式收費

在本教程中,我們將使用 Stripe Checkout 的固定價格方法。

計費周期

在開始之前,值得注意的是 Stripe 沒有默認的計費頻率。每個 Stripe 訂閱的計費日期由以下兩個因素決定:

  1. 計費周期錨(訂閱創建的時間戳)
  2. 重複間隔(每天、每月、每年等)

例如,將每月訂閱設置為在每月 2 日循環的客戶將始終在 2 日計費。

如果一個月沒有錨定日,則訂閱將在該月的最後一天計費。例如,從 1 月 31 日開始的訂閱在 2 月 28 日(或閏年的 2 月 29 日),然後是 3 月 31 日、4 月 30 日等等。

要了解有關計費周期的更多信息,請參閱 Stripe 文檔中的設置訂閱計費周期日期頁面。

項目設置

讓我們從為我們的項目創建一個新目錄開始。在該目錄中,我們將創建並激活一個新的虛擬環境並安裝 Flask。

$ mkdir flask-stripe-subscriptions && cd flask-stripe-subscriptions
$ python3.10 -m venv env
$ source env/bin/activate
(env)$ pip install flask

隨意將 virtualenv 和 Pip 換成PoetryPipenv

接下來,創建一個名為 app.py 的文件,並添加基本“Hello World”應用程序的代碼:

# app.py

from flask import Flask, jsonify

app = Flask(__name__)


@app.route("/hello")
def hello_world():
    return jsonify("hello, world!")


if __name__ == "__main__":
    app.run()

啟動服務器:

(env)$ FLASK_ENV=development python app.py

導航到http://localhost:5000/hello,您應該會看到該hello, world!消息。

添加條紋

準備好基礎項目後,讓我們添加 Stripe。安裝最新版本:

(env)$ pip install stripe

接下來,註冊一個 Stipe 帳戶(如果您尚未註冊)並導航到儀表板。單擊“開發人員”,然後從左側邊欄中的列表中單擊“API 密鑰”:

條紋開發人員密鑰

每個 Stripe 帳戶有四個API 密鑰:兩個用於測試,兩個用於生產。每對都有一個“秘密密鑰”和一個“可發布密鑰”。不要向任何人透露密鑰;可發布密鑰將嵌入到任何人都可以看到的頁面上的 JavaScript 中。

目前,右上角“查看測試數據”的切換錶示我們現在正在使用測試鍵。這就是我們想要的。

將您的測試 API 密鑰存儲為環境變量,如下所示:

(env)$ export STRIPE_PUBLISHABLE_KEY=<YOUR_STRIPE_PUBLISHABLE_KEY>
(env)$ export STRIPE_SECRET_KEY=<YOUR_STRIPE_SECRET_KEY>

接下來,將 Stripe 鍵添加到您的應用程序中:

# app.py

import os

import stripe
from flask import Flask, jsonify

app = Flask(__name__)

stripe_keys = {
    "secret_key": os.environ["STRIPE_SECRET_KEY"],
    "publishable_key": os.environ["STRIPE_PUBLISHABLE_KEY"],
}

stripe.api_key = stripe_keys["secret_key"]


@app.route("/hello")
def hello_world():
    return jsonify("hello, world!")


if __name__ == "__main__":
    app.run()

最後在https://dashboard.stripe.com/settings/account的“帳戶設置”中指定一個“帳戶名稱” 。

條帶帳戶名稱

創建產品

接下來,讓我們創建一個訂閱產品來銷售。

單擊“產品”,然後單擊“添加產品”。

添加產品名稱和描述,輸入價格,然後選擇“重複”:

條紋添加產品

點擊“保存產品”。

接下來,獲取價格的 API ID:

條紋添加產品

將 ID 保存為環境變量,如下所示:

(env)$ export STRIPE_PRICE_ID=<YOUR_PRICE_API_ID>

接下來,將其添加到stripe_keys字典中,如下所示:

# app.py

stripe_keys = {
    "secret_key": os.environ["STRIPE_SECRET_KEY"],
    "publishable_key": os.environ["STRIPE_PUBLISHABLE_KEY"],
    "price_id": os.environ["STRIPE_PRICE_ID"],  # new
}

驗證

為了將您的用戶與 Stripe 客戶相關聯並在未來實施訂閱管理,您可能希望在允許客戶訂閱服務之前強制執行用戶身份驗證。Flask-LoginFlask-HTTPAuth是您可以用來管理它的兩個擴展。

查看資源以獲取 Flask 的與身份驗證相關的擴展的完整列表。

除了身份驗證之外,您還需要將一些信息存儲在與客戶相關的數據庫中。您的模型將如下所示:

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    email = db.Column(db.String(128), nullable=False, unique=True)
    password = db.Column(db.String(200), nullable=False)
    created_on = db.Column(db.DateTime, default=func.now(), nullable=False)


class StripeCustomer(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    user_id = database.Column(database.Integer, database.ForeignKey('users.id'))
    stripeCustomerId = db.Column(db.String(255), nullable=False)
    stripeSubscriptionId = db.Column(db.String(255), nullable=False)

如果您願意,請立即進行設置。

獲取可發布密鑰

JavaScript 靜態文件

創建一個名為“static”的新文件夾,然後在該文件夾中添加一個名為main.js的新文件:

(env)$ mkdir static
(env)$ touch static/main.js

向新的main.js文件添加快速健全性檢查:

// static/main.js

console.log("Sanity check!");

接下來,向app.py註冊一個新路由,該路由提供index.html模板:

# app.py

@app.route("/")
def index():
    # you should force the user to log in/sign up
    return render_template("index.html")

不要忘記render_template像這樣在文件頂部導入:

from flask import Flask, jsonify, render_template

對於模板,在項目根目錄中創建一個名為“templates”的新文件夾。在該文件夾中,創建一個名為index.html的新文件並將以下內容放入其中:

<!-- templates/index.html -->

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Flask + Stripe Subscriptions</title>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script>
    <script src="{{ url_for('static', filename='main.js') }}"></script>
  </head>
  <body>
    <div class="container mt-5">
      <button type="submit" class="btn btn-primary" id="submitBtn">Subscribe</button>
    </div>
  </body>
</html>

再次運行服務器:

(env)$ FLASK_ENV=development python app.py

導航到http://localhost:5000/,然後打開 JavaScript 控制台。您應該會在控制台中看到完整性檢查。

JavaScript 健全性檢查

路線

接下來,向app.py添加一個新路由來處理 AJAX 請求:

# app.py

@app.route("/config")
def get_publishable_key():
    stripe_config = {"publicKey": stripe_keys["publishable_key"]}
    return jsonify(stripe_config)

AJAX 請求

接下來,使用Fetch API向static/main.js/config中的新端點發出 AJAX 請求:

// static/main.js

console.log("Sanity check!");

// new
// Get Stripe publishable key
fetch("/config")
.then((result) => { return result.json(); })
.then((data) => {
  // Initialize Stripe.js
  const stripe = Stripe(data.publicKey);
});

請求的響應fetchReadableStreamresult.json()返回一個承諾,我們將其解析為一個 JavaScript 對象——即data. 然後我們使用點符號來訪問publicKey以獲得可發布的密鑰。

templates/index.html中包含Stripe.js ,如下所示:

<!-- templates/index.html -->

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Flask + Stripe Subscriptions</title>
    <script src="https://js.stripe.com/v3/"></script>  <!-- new -->
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script>
    <script src="{{ url_for('static', filename='main.js') }}"></script>
  </head>
  <body>
    <div class="container mt-5">
      <button type="submit" class="btn btn-primary" id="submitBtn">Subscribe</button>
    </div>
  </body>
</html>

現在,在頁面加載後,將調用/config,它將以 Stripe 可發布鍵響應。然後我們將使用這個鍵來創建一個新的 Stripe.js 實例。

創建結帳會話

接下來,我們需要為按鈕的單擊事件附加一個事件處理程序,該事件處理程序將向服務器發送另一個 AJAX 請求以生成新的 Checkout Session ID。

路線

首先,添加新路由:

# app.py

@app.route("/create-checkout-session")
def create_checkout_session():
    domain_url = "http://localhost:5000/"
    stripe.api_key = stripe_keys["secret_key"]

    try:
        checkout_session = stripe.checkout.Session.create(
            # you should get the user id here and pass it along as 'client_reference_id'
            #
            # this will allow you to associate the Stripe session with
            # the user saved in your database
            #
            # example: client_reference_id=user.id,
            success_url=domain_url + "success?session_id={CHECKOUT_SESSION_ID}",
            cancel_url=domain_url + "cancel",
            payment_method_types=["card"],
            mode="subscription",
            line_items=[
                {
                    "price": stripe_keys["price_id"],
                    "quantity": 1,
                }
            ]
        )
        return jsonify({"sessionId": checkout_session["id"]})
    except Exception as e:
        return jsonify(error=str(e)), 403

完整的文件現在應該如下所示:

# app.py

import os

import stripe
from flask import Flask, jsonify, render_template

app = Flask(__name__)

stripe_keys = {
    "secret_key": os.environ["STRIPE_SECRET_KEY"],
    "publishable_key": os.environ["STRIPE_PUBLISHABLE_KEY"],
    "price_id": os.environ["STRIPE_PRICE_ID"],
}

stripe.api_key = stripe_keys["secret_key"]


@app.route("/hello")
def hello_world():
    return jsonify("hello, world!")


@app.route("/")
def index():
    # you should force the user to log in/sign up
    return render_template("index.html")


@app.route("/config")
def get_publishable_key():
    stripe_config = {"publicKey": stripe_keys["publishable_key"]}
    return jsonify(stripe_config)


@app.route("/create-checkout-session")
def create_checkout_session():
    domain_url = "http://localhost:5000/"
    stripe.api_key = stripe_keys["secret_key"]

    try:
        checkout_session = stripe.checkout.Session.create(
            # you should get the user id here and pass it along as 'client_reference_id'
            #
            # this will allow you to associate the Stripe session with
            # the user saved in your database
            #
            # example: client_reference_id=user.id,
            success_url=domain_url + "success?session_id={CHECKOUT_SESSION_ID}",
            cancel_url=domain_url + "cancel",
            payment_method_types=["card"],
            mode="subscription",
            line_items=[
                {
                    "price": stripe_keys["price_id"],
                    "quantity": 1,
                }
            ]
        )
        return jsonify({"sessionId": checkout_session["id"]})
    except Exception as e:
        return jsonify(error=str(e)), 403


if __name__ == "__main__":
    app.run()

AJAX 請求

將事件處理程序和後續 AJAX 請求添加到static/main.js

// static/main.js

console.log("Sanity check!");

// Get Stripe publishable key
fetch("/config")
.then((result) => { return result.json(); })
.then((data) => {
  // Initialize Stripe.js
  const stripe = Stripe(data.publicKey);

  // new
  // Event handler
  document.querySelector("#submitBtn").addEventListener("click", () => {
    // Get Checkout Session ID
    fetch("/create-checkout-session")
    .then((result) => { return result.json(); })
    .then((data) => {
      console.log(data);
      // Redirect to Stripe Checkout
      return stripe.redirectToCheckout({sessionId: data.sessionId})
    })
    .then((res) => {
      console.log(res);
    });
  });
});

在這裡,在解決了result.json()promise 之後,我們使用已解決的 promise中的 Checkout Session ID調用了redirectToCheckout 。

導航到http://localhost:5000/。單擊按鈕時,您應該被重定向到帶有訂閱信息的 Stripe Checkout 實例(用於安全收集付款信息的 Stripe 託管頁面):

條紋結帳

我們可以使用Stripe 提供的幾個測試卡號之一來測試表單。讓我們使用4242 4242 4242 4242. 確保到期日期在未來。為 CVC 添加任意 3 個數字,為郵政編碼添加任意 5 個數字。輸入任何電子郵件地址和姓名。如果一切順利,應該會處理付款並且您應該訂閱,但重定向將失敗,因為我們還沒有設置/success/URL。

用戶重定向

接下來,我們將創建成功並取消視圖,並在結帳後將用戶重定向到適當的頁面。

路線:

# app.py

@app.route("/success")
def success():
    return render_template("success.html")


@app.route("/cancel")
def cancelled():
    return render_template("cancel.html")

同時創建success.htmlcancel.html模板。

成功:

<!-- templates/success.html -->

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Flask + Stripe Subscriptions</title>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script>
  </head>
  <body>
    <div class="container mt-5">
      <p>You have successfully subscribed!</p>
    </div>
  </body>
</html>

取消:

<!-- templates/cancel.html -->

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Flask + Stripe Subscriptions</title>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script>
  </head>
  <body>
    <div class="container mt-5">
      <p>You have cancelled the checkout.</p>
    </div>
  </body>
</html>

/success如果付款通過並且cancel/付款失敗,用戶現在應該被重定向到。測試一下。

條紋網絡鉤子

我們的應用程序在這一點上運行良好,但我們仍然無法以編程方式確認付款。將來我們還需要在客戶成功訂閱時將相關信息保存到數據庫中。我們已經在用戶結帳後將他們重定向到成功頁面,但我們不能單獨依賴該頁面,因為付款確認是異步發生的。

Stripe 和編程一般有兩種類型的事件。具有即時效果和結果的同步事件(例如,創建客戶)和不具有即時結果的異步事件(例如,確認付款)。由於付款確認是異步完成的,因此用戶可能會在確認付款之前和我們收到他們的資金之前被重定向到成功頁面。

在付款完成時獲得通知的最簡單方法之一是使用回調或所謂的 Stripe webhook。我們需要在我們的應用程序中創建一個簡單的端點,當事件發生時(例如,當用戶訂閱時),Stripe 將調用該端點。通過使用 webhook,我們可以絕對確定付款成功。

為了使用 webhook,我們需要:

  1. 設置 webhook 端點
  2. 使用Stripe CLI測試端點
  3. 向 Stripe 註冊端點

端點

創建一個名為的新路由stripe_webhook,每次有人訂閱時都會打印一條消息。

如果您添加了身份驗證,您應該在StripeCustomer表中創建一條記錄,而不是打印一條消息。

# app.py

@app.route("/webhook", methods=["POST"])
def stripe_webhook():
    payload = request.get_data(as_text=True)
    sig_header = request.headers.get("Stripe-Signature")

    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, stripe_keys["endpoint_secret"]
        )

    except ValueError as e:
        # Invalid payload
        return "Invalid payload", 400
    except stripe.error.SignatureVerificationError as e:
        # Invalid signature
        return "Invalid signature", 400

    # Handle the checkout.session.completed event
    if event["type"] == "checkout.session.completed":
        session = event["data"]["object"]

        # Fulfill the purchase...
        handle_checkout_session(session)

    return "Success", 200


def handle_checkout_session(session):
    # here you should fetch the details from the session and save the relevant information
    # to the database (e.g. associate the user with their subscription)
    print("Subscription was successful.")

不要忘記request像這樣在文件頂部導入:

from flask import Flask, jsonify, render_template, request

stripe_webhook現在充當我們的 webhook 端點。在這裡,我們只查找checkout.session.completed在結帳成功時調用的事件,但您可以對其他Stripe 事件使用相同的模式。

測試網絡鉤子

我們將使用Stripe CLI來測試 webhook。

下載並安裝後,在新的終端窗口中運行以下命令以登錄到您的 Stripe 帳戶:

$ stripe login

此命令應生成配對代碼:

Your pairing code is: peach-loves-classy-cozy
This pairing code verifies your authentication with Stripe.
Press Enter to open the browser (^C to quit)

通過按 Enter,CLI 將打開您的默認 Web 瀏覽器並請求訪問您的帳戶信息的權限。繼續並允許訪問。回到您的終端,您應該會看到類似於以下內容的內容:

> Done! The Stripe CLI is configured for Flask Test with account id acct_<ACCOUNT_ID>

Please note: this key will expire after 90 days, at which point you'll need to re-authenticate.

接下來,我們可以開始監聽 Stripe 事件並使用以下命令將它們轉發到我們的端點:

$ stripe listen --forward-to localhost:5000/webhook

這還將生成一個 webhook 簽名密鑰:

> Ready! Your webhook signing secret is whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (^C to quit)

為了初始化端點,設置一個新的環境變量:

(env)$ export STRIPE_ENDPOINT_SECRET=<YOUR_STRIPE_ENDPOINT_SECRET>

接下來,將其添加到stripe_keys字典中,如下所示:

# app.py

stripe_keys = {
    "secret_key": os.environ["STRIPE_SECRET_KEY"],
    "publishable_key": os.environ["STRIPE_PUBLISHABLE_KEY"],
    "price_id": os.environ["STRIPE_PRICE_ID"],
    "endpoint_secret": os.environ["STRIPE_ENDPOINT_SECRET"],  # new
}

Stripe 現在會將事件轉發到我們的端點。要進行測試,請通過 with 運行另一次測試付款4242 4242 4242 4242。在您的終端中,您應該會看到該Subscription was successful.消息。

完成後,停止該stripe listen --forward-to localhost:5000/webhook過程。

註冊端點

最後,在部署您的應用程序後,您可以在 Stripe 儀表板中的Developers > Webhooks下註冊端點。這將生成一個 webhook 簽名密鑰,用於您的生產應用程序。

例如:

燒瓶網絡鉤子

獲取訂閱數據

如果您添加了身份驗證,您可能希望獲取用戶的訂閱數據並將其顯示給他們。

例如:

@app.route("/")
@login_required  # force the user to log in/sign up
def index():
    # check if a record exists for them in the StripeCustomer table
    customer = StripeCustomer.query.filter_by(user_id=current_user.id).first()

    # if record exists, add the subscription info to the render_template method
    if customer:
        subscription = stripe.Subscription.retrieve(customer.stripeSubscriptionId)
        product = stripe.Product.retrieve(subscription.plan.product)
        context = {
            "subscription": subscription,
            "product": product,
        }
        return render_template("index.html", **context)

    return render_template("index.html")

在這裡,如果StripeCustomer存在,我們使用subscriptionId來從 Stripe API 獲取客戶的訂閱和產品信息。

接下來,修改index.html模板以向訂閱用戶顯示當前計劃:

<!-- templates/index.html -->

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Flask + Stripe Subscriptions</title>
    <script src="https://js.stripe.com/v3/"></script>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script>
    <script src="{{ url_for('static', filename='main.js') }}"></script>
  </head>
  <body>
    <div class="container mt-5">
      {% if subscription and subscription.status == "active" %}
        <h4>Your subscription:</h4>
        <div class="card" style="width: 18rem;">
          <div class="card-body">
            <h5 class="card-title">{{ product.name }}</h5>
            <p class="card-text">
              {{ product.description }}
            </p>
          </div>
        </div>
      {% else %}
        <button type="submit" class="btn btn-primary" id="submitBtn">Subscribe</button>
      {% endif %}
    </div>
  </body>
</html>

我們的訂閱客戶現在將看到他們當前的訂閱計劃,而其他人仍會看到訂閱按鈕:

訂閱視圖

限制用戶訪問

如果您想將特定視圖的訪問權限限制為僅訂閱用戶,您可以像我們在上一步中所做的那樣獲取訂閱並檢查是否subscription.status == "active". 通過執行此檢查,您將確保訂閱仍處於活動狀態,這意味著它已支付且尚未取消。

其他可能的訂閱狀態incompleteincomplete_expiredtrialingactivepast_duecanceledunpaid

結論

我們已經成功創建了一個 Flask Web 應用程序,允許用戶訂閱我們的服務。客戶還將每月自動計費。

這只是基礎知識。您仍然需要:

  • 實現用戶認證並限制一些路由
  • 創建一個包含用戶訂閱的數據庫
  • 允許用戶管理/取消他們當前的計劃
  • 處理未來的付款失敗

從GitHub 上的flask-stripe-subscriptions存儲庫中獲取代碼。

來源:  https ://testdriven.io

#flask #python #stripe 

What is GEEK

Buddha Community

如何Flask Stripe 訂閱”
August  Larson

August Larson

1661358540

How to Create Customer Balances for Spark Laravel & Stripe

Introduction

In the following documentation, we will discuss how to configure a Laravel Spark installation when using the Stripe (opens new window)payment provider. All of Spark's configuration options are housed in your application's config/spark.php configuration file.

#Stripe Configuration

Of course, to use Stripe as a payment provider for your Laravel Spark application you must have an active Stripe account (opens new window).

#Environment Variables

Next, you should configure the application environment variables that will be needed by Spark in order to access your Stripe account. These variables should be placed in your application's .env environment file.

Of course, you should adjust the variable's values to correspond to your own Stripe account's credentials. Your Stripe API credentials and public key are available in your Stripe account dashboard:

CASHIER_CURRENCY=USD
CASHIER_CURRENCY_LOCALE=en
STRIPE_KEY=pk_test_example
STRIPE_SECRET=sk_test_example
STRIPE_WEBHOOK_SECRET=sk_test_example

Configuring Locales

In order to use locales other than en, ensure the ext-intl PHP extension is installed and configured on your server.

#Stripe Webhooks

In addition, your Spark powered application will need to receive webhooks from Stripe in order to keep your application's billing and subscription data in sync with Stripe's. Within your Stripe dashboard's webhook management panel, you should configure Stripe to send webhook alerts to your application's /spark/webhook URI. You should enable webhook alerts for the following events:

  • customer.deleted
  • customer.subscription.created
  • customer.subscription.deleted
  • customer.subscription.updated
  • customer.updated
  • invoice.payment_action_required
  • invoice.payment_succeeded

#Webhooks & Local Development

For Stripe to be able to send your application webhooks during local development, you will need to expose your application via a site sharing service such as Ngrok (opens new window)or Expose (opens new window). If you are developing your application locally using Laravel Sail (opens new window), you may use Sail's site sharing command (opens new window).

#Configuring Billables

Spark allows you to define the types of billable models that your application will be managing. Most commonly, applications bill individual users for monthly and yearly subscription plans. However, your application may choose to bill some other type of model, such as a team, organization, band, etc. The Stripe edition of Spark currently only supports a single billable model entity (team, user, etc.) per application.

You may define your billable models within the billables array of your application's spark configuration file. By default, this array contains an entry for the App\Models\User model. If the billable model is something other than App\Models\User, you should invoke Cashier's useCustomerModel method in the boot method of your AppServiceProvider class in order to inform Cashier of your custom model:

use App\Entities\User;
use Laravel\Cashier\Cashier;
 
/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    Cashier::useCustomerModel(User::class);
}

Before continuing, you should ensure that the model class that corresponds to your billable model is using the Spark\Billable trait and that it casts the trial_ends_at attribute to datetime:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Spark\Billable;

class User extends Authenticatable
{
    use Billable, HasFactory, Notifiable;

    protected $casts = [
        'trial_ends_at' => 'datetime',
    ];

}

#Billable Slugs

As you may have noticed, each entry in the billables configuration array is keyed by a "slug" that is a shortened form of the billable model class. This slug can be used when accessing the Spark customer billing portal, such as https://example.com/billing/user or https://example.com/billing/team.

#Billable Resolution

When you installed Laravel Spark, an App\Providers\SparkServiceProvider class was created for you. Within this service provider, you will find a callback that is used by Spark to resolve the billable model instance when accessing the Spark billing portal. By default, this callback simply returns the currently authenticated user, which is the desired behavior for most applications using Laravel Spark:

use App\Models\User;
use Illuminate\Http\Request;
use Spark\Spark;

Spark::billable(User::class)->resolve(function (Request $request) {
    return $request->user();
});

However, if your application is not billing individual users, you may need to adjust this callback. For example, if your application offers team billing instead of user billing, you might customize the callback like so:

use App\Models\Team;
use Illuminate\Http\Request;
use Spark\Spark;

Spark::billable(Team::class)->resolve(function (Request $request) {
    return $request->user()->currentTeam;
});

#Billable Authorization

Next, let's examine the authorization callbacks that Spark will use to determine if the currently authenticated user of your application is authorized to view the billing portal for a particular billable model.

When you installed Laravel Spark, an App\Providers\SparkServiceProvider class was created for you. Within this service provider, you will find the authorization callback definition used to determine if a given user is authorized to view the billing portal for the App\Models\User billable class. Of course, if your application is not billing users, you should update the billable class and authorization callback logic to fit your application's needs. By default, Spark will simply verify that the currently authenticated user can only manage its own billing settings:

use App\Models\User;
use Illuminate\Http\Request;
use Spark\Spark;

Spark::billable(User::class)->authorize(function (User $billable, Request $request) {
    return $request->user() &&
           $request->user()->id == $billable->id;
});

If the authorization callback returns true, the currently authenticated user will be authorized to view the billing portal and manage the billing settings for the given $billable model. If the callback returns false, the request to access the billing portal will be denied.

You are free to customize the authorize callback based on your own application's needs. For example, if your application bills teams instead of individual users, you might update the callback like so:

use App\Models\Team;
use Illuminate\Http\Request;
use Spark\Spark;

Spark::billable(Team::class)->authorize(function (Team $billable, Request $request) {
    return $request->user() &&
           $request->user()->ownsTeam($billable);
});

#Defining Subscription Plans

As we previously discussed, Spark allows you to define the types of billable models that your application will be managing. These billable models are defined within the billables array of your application's config/spark.php configuration file:

Each billable configuration within the billables array contains a plans array. Within this array you may configure each of the billing plans offered by your application to that particular billable type. The monthly_id and yearly_id identifiers should correspond to the price / plan identifiers configured within your Stripe account dashboard:

use App\Models\User;

'billables' => [
    'user' => [
        'model' => User::class,
        'trial_days' => 5,
        'plans' => [
            [
                'name' => 'Standard',
                'short_description' => 'This is a short, human friendly description of the plan.',
                'monthly_id' => 'price_id',
                'yearly_id' => 'price_id',
                'features' => [
                    'Feature 1',
                    'Feature 2',
                    'Feature 3',
                ],
            ],
        ],
    ],
]

If your subscription plan only offers a monthly billing cycle, you may omit the yearly_id identifier from your plan configuration. Likewise, if your plan only offers a yearly billing cycle, you may omit the monthly_id identifier.

In addition, you are free to supply a short description of the plan and a list of features relevant to the plan. This information will be displayed in the Spark billing portal.

#Accessing The Billing Portal

Once you have configured your Spark installation, you may access your application's billing portal at the /billing URI. So, if your application is being served on localhost, you may access your application's billing portal at http://localhost/billing.

Of course, you may link to the billing portal from your application's dashboard however you see fit:

<a href="/billing">
    Manage Subscription
</a>

#Showing A Link To The Terms And Conditions

Many applications display billing terms and conditions during checkout. Spark allows you to easily do the same within your application's billing portal. To get started, add a terms_url configuration value in your application's config/spark.php configuration file:

'terms_url' => '/terms'

Once added, Spark will display a link pointing to /terms in the billing portal.

#Customer Balance Top Ups

Spark Stripe allows your customers to "top up" their balance. This feature can prove useful if your customer's cards do not support recurring payments, such as customers under India's RBI regulations.

To get started, you will need to create a special product and price in your Stripe Dashboard. So, create a product called "Balance Top Up" and add a price to the product that utilizes the "Customer chooses price" Stripe pricing model. After creating the product and price, define the price ID as an environment variable in your application's .env file:

SPARK_TOP_UP_PRICE=price_xxx

After defining the environment variable, enable the top up feature using its corresponding feature flag in your application's config/spark.php configuration file:

'features' => [
    ...
    Features::topups(['price' => env('SPARK_TOPUP_PRICE')]),
    ...
],

Once this feature has been enabled, the balance top up button will be shown in the Spark billing portal. After clicking the balance top up button, the customer will be redirected to a Stripe Checkout session where they can choose the monetary amount they wish to add to their account. Once the customer has completed the Checkout session, the customer will be redirected back to the Spark billing portal and their balance will be updated. You should ensure that your Stripe webhooks are configured to dispatch the checkout.session.completed event.

Invoices are not generated for balance top ups, as invoicing only occurs when the billing cycle renews. Customers that need a refund for a balance top up will need to contact your application's customer support, and the charge can then be refunded manually from the Stripe dashboard.

Link: https://spark.laravel.com/docs/2.x/spark-stripe/configuration.html

#stripe #laravel #php

工藤  晃

工藤 晃

1659960360

如何Flask Stripe 訂閱”

本教程介紹如何使用 Flask 和Stripe設置和收取每月定期訂閱付款。

條紋訂閱付款選項

有多種方法可以實現和處理 Stripe 訂閱,但最著名的兩種是:

  1. 固定價格訂閱
  2. 未來付款

在這兩種情況下,您都可以使用Stripe Checkout(Stripe 託管的結帳頁面)或Stripe Elements(一組用於構建支付表單的自定義 UI 組件)。如果您不介意將用戶重定向到 Stripe 託管的頁面並希望 Stripe 為您處理大部分付款流程(例如,創建客戶和付款意圖等),請使用 Stripe Checkout,否則請使用 Stripe Elements。

固定價格方法更容易設置,但您無法完全控制計費周期和付款。通過使用這種方法,Stripe 將在成功結賬後自動開始向您的客戶收取每個計費周期的費用。

固定價格步驟:

  1. 將用戶重定向到 Stripe Checkout(使用mode=subscription
  2. 創建一個監聽的 webhookcheckout.session.completed
  3. 調用 webhook 後,將相關數據保存到您的數據庫中

未來的付款方式更難設置,但這種方式讓您可以完全控制訂閱。您提前收集客戶詳細信息和付款信息,並在未來某個日期向您的客戶收費。這種方法還允許您同步計費周期,以便您可以在同一天向所有客戶收費。

未來的付款步驟:

  1. 將用戶重定向到 Stripe Checkout(帶有mode=setup)以收集付款信息
  2. 創建一個監聽的 webhookcheckout.session.completed
  3. 調用 webhook 後,將相關數據保存到您的數據庫中
  4. 從那裡,您可以使用Payment Intents API在未來的某個日期向付款方式收費

在本教程中,我們將使用 Stripe Checkout 的固定價格方法。

計費周期

在開始之前,值得注意的是 Stripe 沒有默認的計費頻率。每個 Stripe 訂閱的計費日期由以下兩個因素決定:

  1. 計費周期錨(訂閱創建的時間戳)
  2. 重複間隔(每天、每月、每年等)

例如,將每月訂閱設置為在每月 2 日循環的客戶將始終在 2 日計費。

如果一個月沒有錨定日,則訂閱將在該月的最後一天計費。例如,從 1 月 31 日開始的訂閱在 2 月 28 日(或閏年的 2 月 29 日),然後是 3 月 31 日、4 月 30 日等等。

要了解有關計費周期的更多信息,請參閱 Stripe 文檔中的設置訂閱計費周期日期頁面。

項目設置

讓我們從為我們的項目創建一個新目錄開始。在該目錄中,我們將創建並激活一個新的虛擬環境並安裝 Flask。

$ mkdir flask-stripe-subscriptions && cd flask-stripe-subscriptions
$ python3.10 -m venv env
$ source env/bin/activate
(env)$ pip install flask

隨意將 virtualenv 和 Pip 換成PoetryPipenv

接下來,創建一個名為 app.py 的文件,並添加基本“Hello World”應用程序的代碼:

# app.py

from flask import Flask, jsonify

app = Flask(__name__)


@app.route("/hello")
def hello_world():
    return jsonify("hello, world!")


if __name__ == "__main__":
    app.run()

啟動服務器:

(env)$ FLASK_ENV=development python app.py

導航到http://localhost:5000/hello,您應該會看到該hello, world!消息。

添加條紋

準備好基礎項目後,讓我們添加 Stripe。安裝最新版本:

(env)$ pip install stripe

接下來,註冊一個 Stipe 帳戶(如果您尚未註冊)並導航到儀表板。單擊“開發人員”,然後從左側邊欄中的列表中單擊“API 密鑰”:

條紋開發人員密鑰

每個 Stripe 帳戶有四個API 密鑰:兩個用於測試,兩個用於生產。每對都有一個“秘密密鑰”和一個“可發布密鑰”。不要向任何人透露密鑰;可發布密鑰將嵌入到任何人都可以看到的頁面上的 JavaScript 中。

目前,右上角“查看測試數據”的切換錶示我們現在正在使用測試鍵。這就是我們想要的。

將您的測試 API 密鑰存儲為環境變量,如下所示:

(env)$ export STRIPE_PUBLISHABLE_KEY=<YOUR_STRIPE_PUBLISHABLE_KEY>
(env)$ export STRIPE_SECRET_KEY=<YOUR_STRIPE_SECRET_KEY>

接下來,將 Stripe 鍵添加到您的應用程序中:

# app.py

import os

import stripe
from flask import Flask, jsonify

app = Flask(__name__)

stripe_keys = {
    "secret_key": os.environ["STRIPE_SECRET_KEY"],
    "publishable_key": os.environ["STRIPE_PUBLISHABLE_KEY"],
}

stripe.api_key = stripe_keys["secret_key"]


@app.route("/hello")
def hello_world():
    return jsonify("hello, world!")


if __name__ == "__main__":
    app.run()

最後在https://dashboard.stripe.com/settings/account的“帳戶設置”中指定一個“帳戶名稱” 。

條帶帳戶名稱

創建產品

接下來,讓我們創建一個訂閱產品來銷售。

單擊“產品”,然後單擊“添加產品”。

添加產品名稱和描述,輸入價格,然後選擇“重複”:

條紋添加產品

點擊“保存產品”。

接下來,獲取價格的 API ID:

條紋添加產品

將 ID 保存為環境變量,如下所示:

(env)$ export STRIPE_PRICE_ID=<YOUR_PRICE_API_ID>

接下來,將其添加到stripe_keys字典中,如下所示:

# app.py

stripe_keys = {
    "secret_key": os.environ["STRIPE_SECRET_KEY"],
    "publishable_key": os.environ["STRIPE_PUBLISHABLE_KEY"],
    "price_id": os.environ["STRIPE_PRICE_ID"],  # new
}

驗證

為了將您的用戶與 Stripe 客戶相關聯並在未來實施訂閱管理,您可能希望在允許客戶訂閱服務之前強制執行用戶身份驗證。Flask-LoginFlask-HTTPAuth是您可以用來管理它的兩個擴展。

查看資源以獲取 Flask 的與身份驗證相關的擴展的完整列表。

除了身份驗證之外,您還需要將一些信息存儲在與客戶相關的數據庫中。您的模型將如下所示:

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    email = db.Column(db.String(128), nullable=False, unique=True)
    password = db.Column(db.String(200), nullable=False)
    created_on = db.Column(db.DateTime, default=func.now(), nullable=False)


class StripeCustomer(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    user_id = database.Column(database.Integer, database.ForeignKey('users.id'))
    stripeCustomerId = db.Column(db.String(255), nullable=False)
    stripeSubscriptionId = db.Column(db.String(255), nullable=False)

如果您願意,請立即進行設置。

獲取可發布密鑰

JavaScript 靜態文件

創建一個名為“static”的新文件夾,然後在該文件夾中添加一個名為main.js的新文件:

(env)$ mkdir static
(env)$ touch static/main.js

向新的main.js文件添加快速健全性檢查:

// static/main.js

console.log("Sanity check!");

接下來,向app.py註冊一個新路由,該路由提供index.html模板:

# app.py

@app.route("/")
def index():
    # you should force the user to log in/sign up
    return render_template("index.html")

不要忘記render_template像這樣在文件頂部導入:

from flask import Flask, jsonify, render_template

對於模板,在項目根目錄中創建一個名為“templates”的新文件夾。在該文件夾中,創建一個名為index.html的新文件並將以下內容放入其中:

<!-- templates/index.html -->

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Flask + Stripe Subscriptions</title>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script>
    <script src="{{ url_for('static', filename='main.js') }}"></script>
  </head>
  <body>
    <div class="container mt-5">
      <button type="submit" class="btn btn-primary" id="submitBtn">Subscribe</button>
    </div>
  </body>
</html>

再次運行服務器:

(env)$ FLASK_ENV=development python app.py

導航到http://localhost:5000/,然後打開 JavaScript 控制台。您應該會在控制台中看到完整性檢查。

JavaScript 健全性檢查

路線

接下來,向app.py添加一個新路由來處理 AJAX 請求:

# app.py

@app.route("/config")
def get_publishable_key():
    stripe_config = {"publicKey": stripe_keys["publishable_key"]}
    return jsonify(stripe_config)

AJAX 請求

接下來,使用Fetch API向static/main.js/config中的新端點發出 AJAX 請求:

// static/main.js

console.log("Sanity check!");

// new
// Get Stripe publishable key
fetch("/config")
.then((result) => { return result.json(); })
.then((data) => {
  // Initialize Stripe.js
  const stripe = Stripe(data.publicKey);
});

請求的響應fetchReadableStreamresult.json()返回一個承諾,我們將其解析為一個 JavaScript 對象——即data. 然後我們使用點符號來訪問publicKey以獲得可發布的密鑰。

templates/index.html中包含Stripe.js ,如下所示:

<!-- templates/index.html -->

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Flask + Stripe Subscriptions</title>
    <script src="https://js.stripe.com/v3/"></script>  <!-- new -->
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script>
    <script src="{{ url_for('static', filename='main.js') }}"></script>
  </head>
  <body>
    <div class="container mt-5">
      <button type="submit" class="btn btn-primary" id="submitBtn">Subscribe</button>
    </div>
  </body>
</html>

現在,在頁面加載後,將調用/config,它將以 Stripe 可發布鍵響應。然後我們將使用這個鍵來創建一個新的 Stripe.js 實例。

創建結帳會話

接下來,我們需要為按鈕的單擊事件附加一個事件處理程序,該事件處理程序將向服務器發送另一個 AJAX 請求以生成新的 Checkout Session ID。

路線

首先,添加新路由:

# app.py

@app.route("/create-checkout-session")
def create_checkout_session():
    domain_url = "http://localhost:5000/"
    stripe.api_key = stripe_keys["secret_key"]

    try:
        checkout_session = stripe.checkout.Session.create(
            # you should get the user id here and pass it along as 'client_reference_id'
            #
            # this will allow you to associate the Stripe session with
            # the user saved in your database
            #
            # example: client_reference_id=user.id,
            success_url=domain_url + "success?session_id={CHECKOUT_SESSION_ID}",
            cancel_url=domain_url + "cancel",
            payment_method_types=["card"],
            mode="subscription",
            line_items=[
                {
                    "price": stripe_keys["price_id"],
                    "quantity": 1,
                }
            ]
        )
        return jsonify({"sessionId": checkout_session["id"]})
    except Exception as e:
        return jsonify(error=str(e)), 403

完整的文件現在應該如下所示:

# app.py

import os

import stripe
from flask import Flask, jsonify, render_template

app = Flask(__name__)

stripe_keys = {
    "secret_key": os.environ["STRIPE_SECRET_KEY"],
    "publishable_key": os.environ["STRIPE_PUBLISHABLE_KEY"],
    "price_id": os.environ["STRIPE_PRICE_ID"],
}

stripe.api_key = stripe_keys["secret_key"]


@app.route("/hello")
def hello_world():
    return jsonify("hello, world!")


@app.route("/")
def index():
    # you should force the user to log in/sign up
    return render_template("index.html")


@app.route("/config")
def get_publishable_key():
    stripe_config = {"publicKey": stripe_keys["publishable_key"]}
    return jsonify(stripe_config)


@app.route("/create-checkout-session")
def create_checkout_session():
    domain_url = "http://localhost:5000/"
    stripe.api_key = stripe_keys["secret_key"]

    try:
        checkout_session = stripe.checkout.Session.create(
            # you should get the user id here and pass it along as 'client_reference_id'
            #
            # this will allow you to associate the Stripe session with
            # the user saved in your database
            #
            # example: client_reference_id=user.id,
            success_url=domain_url + "success?session_id={CHECKOUT_SESSION_ID}",
            cancel_url=domain_url + "cancel",
            payment_method_types=["card"],
            mode="subscription",
            line_items=[
                {
                    "price": stripe_keys["price_id"],
                    "quantity": 1,
                }
            ]
        )
        return jsonify({"sessionId": checkout_session["id"]})
    except Exception as e:
        return jsonify(error=str(e)), 403


if __name__ == "__main__":
    app.run()

AJAX 請求

將事件處理程序和後續 AJAX 請求添加到static/main.js

// static/main.js

console.log("Sanity check!");

// Get Stripe publishable key
fetch("/config")
.then((result) => { return result.json(); })
.then((data) => {
  // Initialize Stripe.js
  const stripe = Stripe(data.publicKey);

  // new
  // Event handler
  document.querySelector("#submitBtn").addEventListener("click", () => {
    // Get Checkout Session ID
    fetch("/create-checkout-session")
    .then((result) => { return result.json(); })
    .then((data) => {
      console.log(data);
      // Redirect to Stripe Checkout
      return stripe.redirectToCheckout({sessionId: data.sessionId})
    })
    .then((res) => {
      console.log(res);
    });
  });
});

在這裡,在解決了result.json()promise 之後,我們使用已解決的 promise中的 Checkout Session ID調用了redirectToCheckout 。

導航到http://localhost:5000/。單擊按鈕時,您應該被重定向到帶有訂閱信息的 Stripe Checkout 實例(用於安全收集付款信息的 Stripe 託管頁面):

條紋結帳

我們可以使用Stripe 提供的幾個測試卡號之一來測試表單。讓我們使用4242 4242 4242 4242. 確保到期日期在未來。為 CVC 添加任意 3 個數字,為郵政編碼添加任意 5 個數字。輸入任何電子郵件地址和姓名。如果一切順利,應該會處理付款並且您應該訂閱,但重定向將失敗,因為我們還沒有設置/success/URL。

用戶重定向

接下來,我們將創建成功並取消視圖,並在結帳後將用戶重定向到適當的頁面。

路線:

# app.py

@app.route("/success")
def success():
    return render_template("success.html")


@app.route("/cancel")
def cancelled():
    return render_template("cancel.html")

同時創建success.htmlcancel.html模板。

成功:

<!-- templates/success.html -->

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Flask + Stripe Subscriptions</title>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script>
  </head>
  <body>
    <div class="container mt-5">
      <p>You have successfully subscribed!</p>
    </div>
  </body>
</html>

取消:

<!-- templates/cancel.html -->

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Flask + Stripe Subscriptions</title>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script>
  </head>
  <body>
    <div class="container mt-5">
      <p>You have cancelled the checkout.</p>
    </div>
  </body>
</html>

/success如果付款通過並且cancel/付款失敗,用戶現在應該被重定向到。測試一下。

條紋網絡鉤子

我們的應用程序在這一點上運行良好,但我們仍然無法以編程方式確認付款。將來我們還需要在客戶成功訂閱時將相關信息保存到數據庫中。我們已經在用戶結帳後將他們重定向到成功頁面,但我們不能單獨依賴該頁面,因為付款確認是異步發生的。

Stripe 和編程一般有兩種類型的事件。具有即時效果和結果的同步事件(例如,創建客戶)和不具有即時結果的異步事件(例如,確認付款)。由於付款確認是異步完成的,因此用戶可能會在確認付款之前和我們收到他們的資金之前被重定向到成功頁面。

在付款完成時獲得通知的最簡單方法之一是使用回調或所謂的 Stripe webhook。我們需要在我們的應用程序中創建一個簡單的端點,當事件發生時(例如,當用戶訂閱時),Stripe 將調用該端點。通過使用 webhook,我們可以絕對確定付款成功。

為了使用 webhook,我們需要:

  1. 設置 webhook 端點
  2. 使用Stripe CLI測試端點
  3. 向 Stripe 註冊端點

端點

創建一個名為的新路由stripe_webhook,每次有人訂閱時都會打印一條消息。

如果您添加了身份驗證,您應該在StripeCustomer表中創建一條記錄,而不是打印一條消息。

# app.py

@app.route("/webhook", methods=["POST"])
def stripe_webhook():
    payload = request.get_data(as_text=True)
    sig_header = request.headers.get("Stripe-Signature")

    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, stripe_keys["endpoint_secret"]
        )

    except ValueError as e:
        # Invalid payload
        return "Invalid payload", 400
    except stripe.error.SignatureVerificationError as e:
        # Invalid signature
        return "Invalid signature", 400

    # Handle the checkout.session.completed event
    if event["type"] == "checkout.session.completed":
        session = event["data"]["object"]

        # Fulfill the purchase...
        handle_checkout_session(session)

    return "Success", 200


def handle_checkout_session(session):
    # here you should fetch the details from the session and save the relevant information
    # to the database (e.g. associate the user with their subscription)
    print("Subscription was successful.")

不要忘記request像這樣在文件頂部導入:

from flask import Flask, jsonify, render_template, request

stripe_webhook現在充當我們的 webhook 端點。在這裡,我們只查找checkout.session.completed在結帳成功時調用的事件,但您可以對其他Stripe 事件使用相同的模式。

測試網絡鉤子

我們將使用Stripe CLI來測試 webhook。

下載並安裝後,在新的終端窗口中運行以下命令以登錄到您的 Stripe 帳戶:

$ stripe login

此命令應生成配對代碼:

Your pairing code is: peach-loves-classy-cozy
This pairing code verifies your authentication with Stripe.
Press Enter to open the browser (^C to quit)

通過按 Enter,CLI 將打開您的默認 Web 瀏覽器並請求訪問您的帳戶信息的權限。繼續並允許訪問。回到您的終端,您應該會看到類似於以下內容的內容:

> Done! The Stripe CLI is configured for Flask Test with account id acct_<ACCOUNT_ID>

Please note: this key will expire after 90 days, at which point you'll need to re-authenticate.

接下來,我們可以開始監聽 Stripe 事件並使用以下命令將它們轉發到我們的端點:

$ stripe listen --forward-to localhost:5000/webhook

這還將生成一個 webhook 簽名密鑰:

> Ready! Your webhook signing secret is whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (^C to quit)

為了初始化端點,設置一個新的環境變量:

(env)$ export STRIPE_ENDPOINT_SECRET=<YOUR_STRIPE_ENDPOINT_SECRET>

接下來,將其添加到stripe_keys字典中,如下所示:

# app.py

stripe_keys = {
    "secret_key": os.environ["STRIPE_SECRET_KEY"],
    "publishable_key": os.environ["STRIPE_PUBLISHABLE_KEY"],
    "price_id": os.environ["STRIPE_PRICE_ID"],
    "endpoint_secret": os.environ["STRIPE_ENDPOINT_SECRET"],  # new
}

Stripe 現在會將事件轉發到我們的端點。要進行測試,請通過 with 運行另一次測試付款4242 4242 4242 4242。在您的終端中,您應該會看到該Subscription was successful.消息。

完成後,停止該stripe listen --forward-to localhost:5000/webhook過程。

註冊端點

最後,在部署您的應用程序後,您可以在 Stripe 儀表板中的Developers > Webhooks下註冊端點。這將生成一個 webhook 簽名密鑰,用於您的生產應用程序。

例如:

燒瓶網絡鉤子

獲取訂閱數據

如果您添加了身份驗證,您可能希望獲取用戶的訂閱數據並將其顯示給他們。

例如:

@app.route("/")
@login_required  # force the user to log in/sign up
def index():
    # check if a record exists for them in the StripeCustomer table
    customer = StripeCustomer.query.filter_by(user_id=current_user.id).first()

    # if record exists, add the subscription info to the render_template method
    if customer:
        subscription = stripe.Subscription.retrieve(customer.stripeSubscriptionId)
        product = stripe.Product.retrieve(subscription.plan.product)
        context = {
            "subscription": subscription,
            "product": product,
        }
        return render_template("index.html", **context)

    return render_template("index.html")

在這裡,如果StripeCustomer存在,我們使用subscriptionId來從 Stripe API 獲取客戶的訂閱和產品信息。

接下來,修改index.html模板以向訂閱用戶顯示當前計劃:

<!-- templates/index.html -->

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Flask + Stripe Subscriptions</title>
    <script src="https://js.stripe.com/v3/"></script>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" crossorigin="anonymous"></script>
    <script src="{{ url_for('static', filename='main.js') }}"></script>
  </head>
  <body>
    <div class="container mt-5">
      {% if subscription and subscription.status == "active" %}
        <h4>Your subscription:</h4>
        <div class="card" style="width: 18rem;">
          <div class="card-body">
            <h5 class="card-title">{{ product.name }}</h5>
            <p class="card-text">
              {{ product.description }}
            </p>
          </div>
        </div>
      {% else %}
        <button type="submit" class="btn btn-primary" id="submitBtn">Subscribe</button>
      {% endif %}
    </div>
  </body>
</html>

我們的訂閱客戶現在將看到他們當前的訂閱計劃,而其他人仍會看到訂閱按鈕:

訂閱視圖

限制用戶訪問

如果您想將特定視圖的訪問權限限制為僅訂閱用戶,您可以像我們在上一步中所做的那樣獲取訂閱並檢查是否subscription.status == "active". 通過執行此檢查,您將確保訂閱仍處於活動狀態,這意味著它已支付且尚未取消。

其他可能的訂閱狀態incompleteincomplete_expiredtrialingactivepast_duecanceledunpaid

結論

我們已經成功創建了一個 Flask Web 應用程序,允許用戶訂閱我們的服務。客戶還將每月自動計費。

這只是基礎知識。您仍然需要:

  • 實現用戶認證並限制一些路由
  • 創建一個包含用戶訂閱的數據庫
  • 允許用戶管理/取消他們當前的計劃
  • 處理未來的付款失敗

從GitHub 上的flask-stripe-subscriptions存儲庫中獲取代碼。

來源:  https ://testdriven.io

#flask #python #stripe 

Stripe Payment Gateway Integration Example In Laravel 8

In this post i will share you stripe payment gateway integration example in laravel 8, stripe payment gateway is integrated in many website for payment collection from client, In this time many e-commerce website and other shopping websites are use stripe payment gateway.

So, here we will learn stripe payment gateway integration in laravel 8.

Read More : Stripe Payment Gateway Integration Example In Laravel 8

https://websolutionstuff.com/post/stripe-payment-gateway-integration-example-in-laravel-8


Read Also : How To Integrate Paypal Payment Gateway In Laravel

https://websolutionstuff.com/post/how-to-integrate-paypal-payment-gateway-in-laravel

#stripe payment gateway integration example in laravel 8 #laravel 8 stripe payment gateway integration example #stripe payment gateway integration in laravel 8 #stripe payment gateway #laravel8 #payment gateway

Adding Stripe to an iOS application

In this episode, you’ll learn how to get started with the stripe-ios client library and how to add Stripe to your iOS application. Thor covers installation of the library and how to make a request to fetch the publishable key from a server.

Table of contents

00:30 Setup a Podfile and xcodeworkspace
01:54 Add the Stripe pod
02:54 How to update Stripe to the latest
04:00 Make a request to a server to fetch a publishable key
10:08 Configure the Stripe SDK with the key

#adding stripe #stripe #ios #ios application

Adding Stripe to your Android project

In this episode, you’ll learn how to get started with the stripe-android client library and how to add Stripe to your Android application. Thor covers installation of the library and how to make a request to fetch the publishable key from a server.

Table of contents

00:00 Install stripe-android with gradle
01:45 Fetch the publishable key
07:40 Setup the PaymentConfiguration

#android project #adding stripe #stripe