Duyen Hoang

Duyen Hoang

1659926940

Cách Xử Lý Các Khoản Thanh Toán Đăng Ký Với Django Và Stripe

Hướng dẫn này xem xét cách xử lý các khoản thanh toán đăng ký với Django và Stripe.

Tùy chọn thanh toán đăng ký Stripe

Có nhiều cách để triển khai và xử lý đăng ký Stripe, nhưng hai cách phổ biến nhất là:

  1. Đăng ký giá cố định
  2. Các khoản thanh toán trong tương lai

Trong cả hai trường hợp, bạn có thể sử dụng Stripe Checkout (là trang thanh toán được lưu trữ trên Stripe) hoặc Stripe Elements (là một tập hợp các thành phần giao diện người dùng tùy chỉnh được sử dụng để tạo biểu mẫu thanh toán). Sử dụng Stripe Checkout nếu bạn không ngại chuyển hướng người dùng của mình đến trang do Stripe lưu trữ và muốn Stripe xử lý hầu hết quy trình thanh toán cho bạn (ví dụ: tạo khách hàng và ý định thanh toán, v.v.), nếu không, hãy sử dụng Stripe Elements.

Phương pháp giá cố định dễ thiết lập hơn nhiều, nhưng bạn không có toàn quyền kiểm soát các chu kỳ lập hóa đơn và thanh toán. Bằng cách sử dụng phương pháp này, Stripe sẽ tự động bắt đầu tính phí khách hàng của bạn mỗi chu kỳ thanh toán sau khi thanh toán thành công.

Các bước giá cố định:

  1. Chuyển hướng người dùng đến Stripe Checkout (với mode=subscription)
  2. Tạo một webhook lắng nghecheckout.session.completed
  3. Sau khi webhook được gọi, hãy lưu dữ liệu có liên quan vào cơ sở dữ liệu của bạn

Phương pháp thanh toán trong tương lai khó thiết lập hơn, nhưng phương pháp này cung cấp cho bạn toàn quyền kiểm soát các đăng ký. Bạn thu thập trước chi tiết khách hàng và thông tin thanh toán và tính phí khách hàng của bạn vào một ngày trong tương lai. Cách tiếp cận này cũng cho phép bạn đồng bộ hóa các chu kỳ thanh toán để bạn có thể tính phí tất cả khách hàng của mình trong cùng một ngày.

Các bước thanh toán trong tương lai:

  1. Chuyển hướng người dùng đến Stripe Checkout (với mode=setup) để thu thập thông tin thanh toán
  2. Tạo một webhook lắng nghecheckout.session.completed
  3. Sau khi webhook được gọi, hãy lưu dữ liệu có liên quan vào cơ sở dữ liệu của bạn
  4. Từ đó, bạn có thể tính phí phương thức thanh toán vào một ngày trong tương lai bằng API ý định thanh toán

Trong hướng dẫn này, chúng tôi sẽ sử dụng phương pháp giá cố định với Stripe Checkout.

Chu kỳ thanh toán

Trước khi tham gia, cần lưu ý rằng Stripe không có tần suất thanh toán mặc định. Ngày thanh toán của mọi đăng ký Stripe được xác định bởi hai yếu tố sau:

  1. cố định chu kỳ thanh toán (dấu thời gian tạo đăng ký)
  2. khoảng thời gian định kỳ (hàng ngày, hàng tháng, hàng năm, v.v.)

Ví dụ: khách hàng có đăng ký hàng tháng được đặt chu kỳ vào ngày 2 của tháng sẽ luôn được lập hóa đơn vào ngày 2.

Nếu một tháng không có ngày cố định, đăng ký sẽ được tính phí vào ngày cuối cùng của tháng. Ví dụ: đăng ký bắt đầu từ ngày 31 tháng 1 sẽ được lập hóa đơn vào ngày 28 tháng 2 (hoặc ngày 29 tháng 2 trong năm nhuận), sau đó là ngày 31 tháng 3, ngày 30 tháng 4, v.v.

Để tìm hiểu thêm về chu kỳ thanh toán, hãy tham khảo trang Đặt ngày chu kỳ thanh toán đăng ký từ tài liệu Stripe.

Thiết lập dự án

Hãy bắt đầu bằng cách tạo một thư mục mới cho dự án của chúng ta. Bên trong thư mục, chúng tôi sẽ tạo và kích hoạt một môi trường ảo mới, cài đặt Django và tạo một dự án Django mới bằng cách sử dụng django-admin:

$ mkdir django-stripe-subscriptions && cd django-stripe-subscriptions
$ python3.10 -m venv env
$ source env/bin/activate

(env)$ pip install django
(env)$ django-admin startproject djangostripe .

Sau đó, tạo một ứng dụng mới có tên subscriptions:

(env)$ python manage.py startapp subscriptions

Đăng ký ứng dụng trong djangostripe / settings.py theo INSTALLED_APPS:

# djangostripe/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'subscriptions.apps.SubscriptionsConfig', # new
]

Tạo một chế độ xem mới được gọi là home, sẽ đóng vai trò là trang chỉ mục chính của chúng tôi:

# subscriptions/views.py

from django.shortcuts import render

def home(request):
    return render(request, 'home.html')

Gán một URL cho chế độ xem bằng cách thêm thông tin sau vào subscribe / urls.py :

# subscriptions/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='subscriptions-home'),
]

Bây giờ, hãy nói với Django rằng subscriptionsứng dụng có các URL riêng bên trong ứng dụng chính:

# djangostripe/urls.py

from django.contrib import admin
from django.urls import path, include # new

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('subscriptions.urls')), # new
]

Cuối cùng, tạo một mẫu mới có tên home.html bên trong một thư mục mới có tên là "mẫu". Thêm HTML sau vào mẫu:

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

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Django + Stripe Subscriptions</title>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
  </head>
  <body>
    <div class="container mt-5">
      <button type="submit" class="btn btn-primary" id="submitBtn">Subscribe</button>
    </div>
  </body>
</html>

Đảm bảo cập nhật tệp settings.py để Django biết tìm thư mục "mẫu":

# djangostripe/settings.py

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': ['templates'], # new
        ...

Cuối cùng chạy migrateđể đồng bộ cơ sở dữ liệu và runserverkhởi động máy chủ web cục bộ của Django.

(env)$ python manage.py migrate
(env)$ python manage.py runserver

Truy cập http: // localhost: 8000 / trong trình duyệt bạn chọn. Bạn sẽ thấy nút "Đăng ký" mà sau này chúng tôi sẽ sử dụng để chuyển hướng khách hàng đến trang Stripe Checkout.

Thêm sọc

Với dự án cơ sở đã sẵn sàng, hãy thêm Stripe. Cài đặt phiên bản mới nhất:

(env)$ pip install stripe

Tiếp theo, đăng ký tài khoản Stipe (nếu bạn chưa làm như vậy) và điều hướng đến trang tổng quan . Nhấp vào "Nhà phát triển" và sau đó từ danh sách ở thanh bên trái, nhấp vào "Khóa API":

Khóa nhà phát triển sọc

Mỗi tài khoản Stripe có bốn khóa API : hai khóa để thử nghiệm và hai khóa để sản xuất. Mỗi cặp có một "khóa bí mật" và một "khóa có thể xuất bản". Không tiết lộ chìa khóa bí mật cho bất kỳ ai; khóa có thể xuất bản sẽ được nhúng vào JavaScript trên trang mà bất kỳ ai cũng có thể nhìn thấy.

Hiện tại, nút chuyển đổi cho "Xem dữ liệu kiểm tra" ở phía trên bên phải cho biết rằng chúng tôi đang sử dụng các khóa kiểm tra ngay bây giờ. Đó là những gì chúng tôi muốn.

Ở cuối tệp settings.py của bạn , thêm hai dòng sau bao gồm khóa bí mật kiểm tra và khóa có thể xuất bản của riêng bạn. Đảm bảo bao gồm các ''ký tự xung quanh các phím thực tế.

# djangostripe/settings.py

STRIPE_PUBLISHABLE_KEY = '<enter your stripe publishable key>'
STRIPE_SECRET_KEY = '<enter your stripe secret key>'

Cuối cùng, bạn sẽ cần chỉ định "Tên tài khoản" trong "Cài đặt tài khoản" tại https://dashboard.stripe.com/settings/account .

Tạo một sản phẩm

Tiếp theo, hãy tạo một sản phẩm đăng ký để bán.

Nhấp vào "Sản phẩm" và sau đó "Thêm sản phẩm".

Thêm tên và mô tả sản phẩm, nhập giá và chọn "Định kỳ":

Sọc Thêm sản phẩm

Nhấp vào "Lưu sản phẩm".

Tiếp theo, lấy ID API của giá:

Sọc Thêm sản phẩm

Lưu ID trong tệp settings.py như sau:

# djangostripe/settings.py

STRIPE_PRICE_ID = '<enter your stripe price id>'

Xác thực

Để liên kết người dùng Django với khách hàng của Stripe và triển khai quản lý đăng ký trong tương lai, chúng tôi cần thực thi xác thực người dùng trước khi cho phép khách hàng đăng ký dịch vụ. Chúng tôi có thể đạt được điều này bằng cách thêm trình @login_requiredtrang trí vào tất cả các chế độ xem yêu cầu xác thực.

Đầu tiên chúng ta hãy bảo vệ homequan điểm:

# subscriptions/views.py

from django.contrib.auth.decorators import login_required  # new
from django.shortcuts import render

@login_required  # new
def home(request):
    return render(request, 'home.html')

Bây giờ, khi người dùng không được xác thực cố gắng truy cập homechế độ xem, họ sẽ được chuyển hướng đến chế độ đã LOGIN_REDIRECT_URLxác định trong settings.py .

Nếu bạn có hệ thống xác thực ưa thích, hãy thiết lập hệ thống đó ngay bây giờ và định cấu hình LOGIN_REDIRECT_URL, nếu không, hãy chuyển sang phần tiếp theo để cài đặt django-allauth .

django-allauth (Tùy chọn)

django-allauth là một trong những gói Django phổ biến nhất để xử lý xác thực, đăng ký, quản lý tài khoản và xác thực tài khoản của bên thứ ba. Chúng tôi sẽ sử dụng nó để cấu hình một hệ thống đăng ký / đăng nhập đơn giản.

Đầu tiên, hãy cài đặt gói:

(env)$ pip install django-allauth

Cập nhật INSTALLED_APPStrong djangostripe / settings.py như vậy:

# djangostripe/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.sites', # new
    'allauth', # new
    'allauth.account', # new
    'allauth.socialaccount', # new
    'subscriptions.apps.SubscriptionsConfig',
]

Tiếp theo, thêm cấu hình django-allauth sau vào djangostripe / settings.py :

# djangostripe/settings.py

AUTHENTICATION_BACKENDS = [
    # Needed to login by username in Django admin, regardless of `allauth`
    'django.contrib.auth.backends.ModelBackend',

    # `allauth` specific authentication methods, such as login by e-mail
    'allauth.account.auth_backends.AuthenticationBackend',
]

# We have to set this variable, because we enabled 'django.contrib.sites'
SITE_ID = 1

# User will be redirected to this page after logging in
LOGIN_REDIRECT_URL = '/'

# If you don't have an email server running yet add this line to avoid any possible errors.
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

Đăng ký các URL ủy quyền:

# djangostripe/urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('subscriptions.urls')),
    path('accounts/', include('allauth.urls')),  # new
]

Áp dụng các di chuyển:

(env)$ python manage.py migrate

Kiểm tra xác thực bằng cách chạy máy chủ và điều hướng đến http://localhost.com:8000/ . Bạn sẽ được chuyển hướng đến trang đăng ký. Tạo một tài khoản và sau đó đăng nhập.

Mô hình cơ sở dữ liệu

Để xử lý khách hàng và đăng ký một cách chính xác, chúng tôi cần lưu trữ một số thông tin trong cơ sở dữ liệu của mình. Hãy tạo một mô hình mới được gọi là mô hình StripeCustomernày sẽ lưu trữ Stripe customerIdsubscriptionIdliên kết nó trở lại với người dùng xác thực Django. Điều này sẽ cho phép chúng tôi tìm nạp dữ liệu đăng ký và khách hàng của mình từ Stripe.

Về mặt lý thuyết, chúng tôi có thể lấy customerIdsubscriptionIdtừ Stripe bất cứ khi nào chúng tôi cần chúng, nhưng điều đó sẽ làm tăng đáng kể cơ hội nhận được tỷ lệ bị giới hạn bởi Stripe.

Hãy tạo mô hình của chúng tôi bên trong subscribe / models.py :

# subscriptions/models.py

from django.contrib.auth.models import User
from django.db import models


class StripeCustomer(models.Model):
    user = models.OneToOneField(to=User, on_delete=models.CASCADE)
    stripeCustomerId = models.CharField(max_length=255)
    stripeSubscriptionId = models.CharField(max_length=255)

    def __str__(self):
        return self.user.username

Đăng ký nó với quản trị viên trong subscribe / admin.py :

# subscriptions/admin.py

from django.contrib import admin
from subscriptions.models import StripeCustomer


admin.site.register(StripeCustomer)

Tạo và áp dụng các di chuyển:

(env)$ python manage.py makemigrations && python manage.py migrate

Nhận khóa dành cho nhà xuất bản

Tệp tĩnh JavaScript

Bắt đầu bằng cách tạo một tệp tĩnh mới để chứa tất cả JavaScript của chúng tôi:

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

Thêm kiểm tra nhanh vào tệp main.js mới :

// static/main.js

console.log("Sanity check!");

Sau đó, cập nhật tệp settings.py để Django biết nơi tìm tệp tĩnh:

# djangostripe/settings.py

STATIC_URL = 'static/'

# for django >= 3.1
STATICFILES_DIRS = [Path(BASE_DIR).joinpath('static')]  # new

# for django < 3.1
# STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]  # new

Thêm thẻ mẫu tĩnh cùng với thẻ tập lệnh mới bên trong mẫu HTML:

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

{% load static %} <!-- new -->

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

Chạy lại máy chủ phát triển. Điều hướng đến http: // localhost: 8000 / và mở bảng điều khiển JavaScript. Bạn sẽ thấy kiểm tra sự tỉnh táo bên trong bảng điều khiển của mình.

Lượt xem

Tiếp theo, thêm một chế độ xem mới vào subscribe / views.py để xử lý yêu cầu AJAX:

# subscriptions/views.py

from django.conf import settings # new
from django.contrib.auth.decorators import login_required
from django.http.response import JsonResponse # new
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt # new


@login_required
def home(request):
    return render(request, 'home.html')


# new
@csrf_exempt
def stripe_config(request):
    if request.method == 'GET':
        stripe_config = {'publicKey': settings.STRIPE_PUBLISHABLE_KEY}
        return JsonResponse(stripe_config, safe=False)

Thêm một URL mới:

# subscriptions/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='subscriptions-home'),
    path('config/', views.stripe_config),  # new
]

Yêu cầu AJAX

Tiếp theo, sử dụng API Tìm nạp để thực hiện yêu cầu AJAX tới /config/điểm cuối mới trong static / main.js :

// 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);
});

Phản hồi từ một fetchyêu cầu là một luồng có thể đọc được . result.json()trả về một lời hứa, mà chúng tôi đã giải quyết cho một đối tượng JavaScript - tức là data,. Sau đó, chúng tôi sử dụng ký hiệu dấu chấm để truy cập publicKeynhằm lấy khóa có thể xuất bản.

Tạo phiên thanh toán

Tiếp tục, chúng ta cần đính kèm một trình xử lý sự kiện vào sự kiện nhấp chuột của nút, điều này sẽ gửi một yêu cầu AJAX khác đến máy chủ để tạo ID phiên Checkout mới.

Lượt xem

Đầu tiên, thêm chế độ xem mới:

# subscriptions/views.py

@csrf_exempt
def create_checkout_session(request):
    if request.method == 'GET':
        domain_url = 'http://localhost:8000/'
        stripe.api_key = settings.STRIPE_SECRET_KEY
        try:
            checkout_session = stripe.checkout.Session.create(
                client_reference_id=request.user.id if request.user.is_authenticated else None,
                success_url=domain_url + 'success?session_id={CHECKOUT_SESSION_ID}',
                cancel_url=domain_url + 'cancel/',
                payment_method_types=['card'],
                mode='subscription',
                line_items=[
                    {
                        'price': settings.STRIPE_PRICE_ID,
                        'quantity': 1,
                    }
                ]
            )
            return JsonResponse({'sessionId': checkout_session['id']})
        except Exception as e:
            return JsonResponse({'error': str(e)})

Ở đây, nếu phương thức yêu cầu là GET, chúng tôi đã xác định a domain_url, gán khóa bí mật Stripe cho stripe.api_key(vì vậy nó sẽ được gửi tự động khi chúng tôi thực hiện yêu cầu tạo Phiên kiểm tra mới), tạo Phiên kiểm tra và gửi lại ID trong phản ứng. Hãy lưu ý success_urlcancel_url. Người dùng sẽ được chuyển hướng trở lại các URL đó trong trường hợp thanh toán thành công hoặc hủy tương ứng. Chúng tôi sẽ sớm thiết lập các chế độ xem đó.

Đừng quên nhập:

import stripe

Toàn bộ tệp bây giờ sẽ trông như thế này:

# subscriptions/views.py

import stripe
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.http.response import JsonResponse
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt


@login_required
def home(request):
    return render(request, 'home.html')


@csrf_exempt
def stripe_config(request):
    if request.method == 'GET':
        stripe_config = {'publicKey': settings.STRIPE_PUBLISHABLE_KEY}
        return JsonResponse(stripe_config, safe=False)


@csrf_exempt
def create_checkout_session(request):
    if request.method == 'GET':
        domain_url = 'http://localhost:8000/'
        stripe.api_key = settings.STRIPE_SECRET_KEY
        try:
            checkout_session = stripe.checkout.Session.create(
                client_reference_id=request.user.id if request.user.is_authenticated else None,
                success_url=domain_url + 'success?session_id={CHECKOUT_SESSION_ID}',
                cancel_url=domain_url + 'cancel/',
                payment_method_types=['card'],
                mode='subscription',
                line_items=[
                    {
                        'price': settings.STRIPE_PRICE_ID,
                        'quantity': 1,
                    }
                ]
            )
            return JsonResponse({'sessionId': checkout_session['id']})
        except Exception as e:
            return JsonResponse({'error': str(e)})

Yêu cầu AJAX

Đăng ký URL phiên thanh toán:

# subscriptions/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='subscriptions-home'),
    path('config/', views.stripe_config),
    path('create-checkout-session/', views.create_checkout_session),  # new
]

Thêm trình xử lý sự kiện và yêu cầu AJAX tiếp theo vào 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
  let submitBtn = document.querySelector("#submitBtn");
  if (submitBtn !== null) {
    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);
      });
    });
  }
});

Ở đây, sau khi giải quyết result.json()lời hứa, chúng tôi đã gọi redirectToCheckout với ID phiên Checkout từ lời hứa đã giải quyết.

Điều hướng đến http: // localhost: 8000 / . Khi nhấp vào nút, bạn sẽ được chuyển hướng đến một phiên bản của Stripe Checkout (một trang được lưu trữ trên Stripe để thu thập thông tin thanh toán một cách an toàn) với thông tin đăng ký:

Kiểm tra sọc

Chúng tôi có thể kiểm tra biểu mẫu bằng cách sử dụng một trong một số số thẻ kiểm tra mà Stripe cung cấp. Hãy sử dụng 4242 4242 4242 4242. Đảm bảo ngày hết hạn trong tương lai. Thêm 3 số bất kỳ cho CVC và 5 số bất kỳ cho mã bưu điện. Nhập bất kỳ địa chỉ email và tên. Nếu mọi việc suôn sẻ, thanh toán sẽ được xử lý và bạn sẽ được đăng ký, nhưng chuyển hướng sẽ không thành công vì chúng tôi chưa thiết lập /success/URL.

Chuyển hướng người dùng

Tiếp theo, chúng tôi sẽ tạo thành công và hủy lượt xem và chuyển hướng người dùng đến trang thích hợp sau khi thanh toán.

Lượt xem:

# subscriptions/views.py

@login_required
def success(request):
    return render(request, 'success.html')


@login_required
def cancel(request):
    return render(request, 'cancel.html')

Tạo các mẫu thành công.htmlhủy bỏ.html .

Thành công:

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

{% load static %}

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Django + Stripe Subscriptions</title>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
  </head>
  <body>
    <div class="container mt-5">
      <p>You have successfully subscribed!</p>
      <p><a href="{% url "subscriptions-home" %}">Return to the dashboard</a></p>
    </div>
  </body>
</html>

Hủy bỏ:

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

{% load static %}

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Django + Stripe Subscriptions</title>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
  </head>
  <body>
    <div class="container mt-5">
      <p>You have cancelled the checkout.</p>
      <p><a href="{% url "subscriptions-home" %}">Return to the dashboard</a></p>
    </div>
  </body>
</html>

Đăng ký các chế độ xem mới bên trong subscribe / urls.py :

# subscriptions/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='subscriptions-home'),
    path('config/', views.stripe_config),
    path('create-checkout-session/', views.create_checkout_session),
    path('success/', views.success),  # new
    path('cancel/', views.cancel),  # new
]

The user should now be redirected to /success if the payment goes through and cancel/ if the payment fails. Test this out.

Stripe Webhooks

Our app works well at this point, but we still can't programmatically confirm payments. We also haven't added a new customer to the StripeCustomer model when a customer subscribes successfully. We already redirect the user to the success page after they check out, but we can't rely on that page alone (as confirmation) since payment confirmation happens asynchronously.

There are two types of events in Stripe and programming in general. Synchronous events, which have an immediate effect and results (e.g., creating a customer), and asynchronous events, which don't have an immediate result (e.g., confirming payments). Because payment confirmation is done asynchronously, the user might get redirected to the success page before their payment is confirmed and before we receive their funds.

One of the easiest ways to get notified when the payment goes through is to use a callback or so-called Stripe webhook. We'll need to create a simple endpoint in our application, which Stripe will call whenever an event occurs (e.g., when a user subscribes). By using webhooks, we can be absolutely sure that the payment went through successfully.

In order to use webhooks, we need to:

  1. Set up the webhook endpoint
  2. Test the endpoint using the Stripe CLI
  3. Register the endpoint with Stripe

Endpoint

Create a new view called stripe_webhook which will create a new StripeCustomer every time someone subscribes to our service:

# subscriptions/views.py

@csrf_exempt
def stripe_webhook(request):
    stripe.api_key = settings.STRIPE_SECRET_KEY
    endpoint_secret = settings.STRIPE_ENDPOINT_SECRET
    payload = request.body
    sig_header = request.META['HTTP_STRIPE_SIGNATURE']
    event = None

    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, endpoint_secret
        )
    except ValueError as e:
        # Invalid payload
        return HttpResponse(status=400)
    except stripe.error.SignatureVerificationError as e:
        # Invalid signature
        return HttpResponse(status=400)

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

        # Fetch all the required data from session
        client_reference_id = session.get('client_reference_id')
        stripe_customer_id = session.get('customer')
        stripe_subscription_id = session.get('subscription')

        # Get the user and create a new StripeCustomer
        user = User.objects.get(id=client_reference_id)
        StripeCustomer.objects.create(
            user=user,
            stripeCustomerId=stripe_customer_id,
            stripeSubscriptionId=stripe_subscription_id,
        )
        print(user.username + ' just subscribed.')

    return HttpResponse(status=200)

stripe_webhook now serves as our webhook endpoint. Here, we're only looking for checkout.session.completed events which are called whenever a checkout is successful, but you can use the same pattern for other Stripe events.

Make the following changes to the imports:

# subscriptions/views.py

import stripe
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User  # new
from django.http.response import JsonResponse, HttpResponse  # updated
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt

from subscriptions.models import StripeCustomer  # new

The only thing left do do to make the endpoint accessible is to register it in urls.py:

# subscriptions/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='subscriptions-home'),
    path('config/', views.stripe_config),
    path('create-checkout-session/', views.create_checkout_session),
    path('success/', views.success),
    path('cancel/', views.cancel),
    path('webhook/', views.stripe_webhook),  # new
]

Testing the webhook

We'll use the Stripe CLI to test the webhook.

Once downloaded and installed, run the following command in a new terminal window to log in to your Stripe account:

$ stripe login

This command should generate a pairing code:

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)

By pressing Enter, the CLI will open your default web browser and ask for permission to access your account information. Go ahead and allow access. Back in your terminal, you should see something similar to:

> Done! The Stripe CLI is configured for Django 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.

Next, we can start listening to Stripe events and forward them to our endpoint using the following command:

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

This will also generate a webhook signing secret:

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

In order to initialize the endpoint, add the secret to the settings.py file:

# djangostribe/settings.py

STRIPE_ENDPOINT_SECRET = '<your webhook signing secret here>'

Stripe will now forward events to our endpoint. To test, run another test payment through with 4242 4242 4242 4242. In your terminal, you should see the <USERNAME> just subscribed. message.

Once done, stop the stripe listen --forward-to localhost:8000/webhook/ process.

Register the endpoint

Finally, after deploying your app, you can register the endpoint in the Stripe dashboard, under Developers > Webhooks. This will generate a webhook signing secret for use in your production app.

For example:

Django webhook

Fetch Subscription Data

Our app now allows users to subscribe to our service, but we still have no way to fetch their subscription data and display it.

Update the home view:

# subscriptions/views.py

@login_required
def home(request):
    try:
        # Retrieve the subscription & product
        stripe_customer = StripeCustomer.objects.get(user=request.user)
        stripe.api_key = settings.STRIPE_SECRET_KEY
        subscription = stripe.Subscription.retrieve(stripe_customer.stripeSubscriptionId)
        product = stripe.Product.retrieve(subscription.plan.product)

        # Feel free to fetch any additional data from 'subscription' or 'product'
        # https://stripe.com/docs/api/subscriptions/object
        # https://stripe.com/docs/api/products/object

        return render(request, 'home.html', {
            'subscription': subscription,
            'product': product,
        })

    except StripeCustomer.DoesNotExist:
        return render(request, 'home.html')

Here, if a StripeCustomer exists, we use the subscriptionId to fetch the customer's subscription and product info from the Stripe API.

Modify the home.html template to display the current plan to subscribed users:

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

{% load static %}

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Django + Stripe Subscriptions</title>
    <script src="https://js.stripe.com/v3/"></script>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
    <script src="{% static 'main.js' %}"></script>
  </head>
  <body>
    <div class="container mt-5">
      {% if 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>

Khách hàng đã đăng ký của chúng tôi giờ đây sẽ thấy gói đăng ký hiện tại của họ, trong khi những người khác vẫn sẽ thấy nút đăng ký:

Chế độ xem đã Đăng ký

Hạn chế quyền truy cập của người dùng

Nếu bạn muốn hạn chế quyền truy cập vào các chế độ xem cụ thể đối với chỉ những người dùng đã đăng ký, bạn có thể tìm nạp đăng ký như chúng tôi đã làm trong bước trước và kiểm tra xem có subscription.status == "active". Bằng cách thực hiện kiểm tra này, bạn sẽ đảm bảo đăng ký vẫn hoạt động, có nghĩa là đăng ký đã được thanh toán và chưa bị hủy.

Các trạng thái đăng ký có thể có incompletekhác là ,,,, hoặc .incomplete_expiredtrialingactivepast_duecanceledunpaid

Sự kết luận

Chúng tôi đã tạo thành công một ứng dụng web Django cho phép người dùng đăng ký dịch vụ của chúng tôi và xem gói của họ. Khách hàng của chúng tôi cũng sẽ được tự động lập hóa đơn hàng tháng.

Đây chỉ là những điều cơ bản. Bạn vẫn cần:

  • Cho phép người dùng quản lý / hủy gói hiện tại của họ
  • Xử lý các lỗi thanh toán trong tương lai

Bạn cũng sẽ muốn sử dụng các biến môi trường cho domain_urlkhóa API và bí mật ký webhook thay vì mã hóa cứng chúng.

Lấy mã từ repo django-sọc-đăng ký trên GitHub.

Nguồn:  https://testdriven.io

#django #python #stripe 

What is GEEK

Buddha Community

Cách Xử Lý Các Khoản Thanh Toán Đăng Ký Với Django Và Stripe
Duyen Hoang

Duyen Hoang

1659926940

Cách Xử Lý Các Khoản Thanh Toán Đăng Ký Với Django Và Stripe

Hướng dẫn này xem xét cách xử lý các khoản thanh toán đăng ký với Django và Stripe.

Tùy chọn thanh toán đăng ký Stripe

Có nhiều cách để triển khai và xử lý đăng ký Stripe, nhưng hai cách phổ biến nhất là:

  1. Đăng ký giá cố định
  2. Các khoản thanh toán trong tương lai

Trong cả hai trường hợp, bạn có thể sử dụng Stripe Checkout (là trang thanh toán được lưu trữ trên Stripe) hoặc Stripe Elements (là một tập hợp các thành phần giao diện người dùng tùy chỉnh được sử dụng để tạo biểu mẫu thanh toán). Sử dụng Stripe Checkout nếu bạn không ngại chuyển hướng người dùng của mình đến trang do Stripe lưu trữ và muốn Stripe xử lý hầu hết quy trình thanh toán cho bạn (ví dụ: tạo khách hàng và ý định thanh toán, v.v.), nếu không, hãy sử dụng Stripe Elements.

Phương pháp giá cố định dễ thiết lập hơn nhiều, nhưng bạn không có toàn quyền kiểm soát các chu kỳ lập hóa đơn và thanh toán. Bằng cách sử dụng phương pháp này, Stripe sẽ tự động bắt đầu tính phí khách hàng của bạn mỗi chu kỳ thanh toán sau khi thanh toán thành công.

Các bước giá cố định:

  1. Chuyển hướng người dùng đến Stripe Checkout (với mode=subscription)
  2. Tạo một webhook lắng nghecheckout.session.completed
  3. Sau khi webhook được gọi, hãy lưu dữ liệu có liên quan vào cơ sở dữ liệu của bạn

Phương pháp thanh toán trong tương lai khó thiết lập hơn, nhưng phương pháp này cung cấp cho bạn toàn quyền kiểm soát các đăng ký. Bạn thu thập trước chi tiết khách hàng và thông tin thanh toán và tính phí khách hàng của bạn vào một ngày trong tương lai. Cách tiếp cận này cũng cho phép bạn đồng bộ hóa các chu kỳ thanh toán để bạn có thể tính phí tất cả khách hàng của mình trong cùng một ngày.

Các bước thanh toán trong tương lai:

  1. Chuyển hướng người dùng đến Stripe Checkout (với mode=setup) để thu thập thông tin thanh toán
  2. Tạo một webhook lắng nghecheckout.session.completed
  3. Sau khi webhook được gọi, hãy lưu dữ liệu có liên quan vào cơ sở dữ liệu của bạn
  4. Từ đó, bạn có thể tính phí phương thức thanh toán vào một ngày trong tương lai bằng API ý định thanh toán

Trong hướng dẫn này, chúng tôi sẽ sử dụng phương pháp giá cố định với Stripe Checkout.

Chu kỳ thanh toán

Trước khi tham gia, cần lưu ý rằng Stripe không có tần suất thanh toán mặc định. Ngày thanh toán của mọi đăng ký Stripe được xác định bởi hai yếu tố sau:

  1. cố định chu kỳ thanh toán (dấu thời gian tạo đăng ký)
  2. khoảng thời gian định kỳ (hàng ngày, hàng tháng, hàng năm, v.v.)

Ví dụ: khách hàng có đăng ký hàng tháng được đặt chu kỳ vào ngày 2 của tháng sẽ luôn được lập hóa đơn vào ngày 2.

Nếu một tháng không có ngày cố định, đăng ký sẽ được tính phí vào ngày cuối cùng của tháng. Ví dụ: đăng ký bắt đầu từ ngày 31 tháng 1 sẽ được lập hóa đơn vào ngày 28 tháng 2 (hoặc ngày 29 tháng 2 trong năm nhuận), sau đó là ngày 31 tháng 3, ngày 30 tháng 4, v.v.

Để tìm hiểu thêm về chu kỳ thanh toán, hãy tham khảo trang Đặt ngày chu kỳ thanh toán đăng ký từ tài liệu Stripe.

Thiết lập dự án

Hãy bắt đầu bằng cách tạo một thư mục mới cho dự án của chúng ta. Bên trong thư mục, chúng tôi sẽ tạo và kích hoạt một môi trường ảo mới, cài đặt Django và tạo một dự án Django mới bằng cách sử dụng django-admin:

$ mkdir django-stripe-subscriptions && cd django-stripe-subscriptions
$ python3.10 -m venv env
$ source env/bin/activate

(env)$ pip install django
(env)$ django-admin startproject djangostripe .

Sau đó, tạo một ứng dụng mới có tên subscriptions:

(env)$ python manage.py startapp subscriptions

Đăng ký ứng dụng trong djangostripe / settings.py theo INSTALLED_APPS:

# djangostripe/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'subscriptions.apps.SubscriptionsConfig', # new
]

Tạo một chế độ xem mới được gọi là home, sẽ đóng vai trò là trang chỉ mục chính của chúng tôi:

# subscriptions/views.py

from django.shortcuts import render

def home(request):
    return render(request, 'home.html')

Gán một URL cho chế độ xem bằng cách thêm thông tin sau vào subscribe / urls.py :

# subscriptions/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='subscriptions-home'),
]

Bây giờ, hãy nói với Django rằng subscriptionsứng dụng có các URL riêng bên trong ứng dụng chính:

# djangostripe/urls.py

from django.contrib import admin
from django.urls import path, include # new

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('subscriptions.urls')), # new
]

Cuối cùng, tạo một mẫu mới có tên home.html bên trong một thư mục mới có tên là "mẫu". Thêm HTML sau vào mẫu:

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

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Django + Stripe Subscriptions</title>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
  </head>
  <body>
    <div class="container mt-5">
      <button type="submit" class="btn btn-primary" id="submitBtn">Subscribe</button>
    </div>
  </body>
</html>

Đảm bảo cập nhật tệp settings.py để Django biết tìm thư mục "mẫu":

# djangostripe/settings.py

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': ['templates'], # new
        ...

Cuối cùng chạy migrateđể đồng bộ cơ sở dữ liệu và runserverkhởi động máy chủ web cục bộ của Django.

(env)$ python manage.py migrate
(env)$ python manage.py runserver

Truy cập http: // localhost: 8000 / trong trình duyệt bạn chọn. Bạn sẽ thấy nút "Đăng ký" mà sau này chúng tôi sẽ sử dụng để chuyển hướng khách hàng đến trang Stripe Checkout.

Thêm sọc

Với dự án cơ sở đã sẵn sàng, hãy thêm Stripe. Cài đặt phiên bản mới nhất:

(env)$ pip install stripe

Tiếp theo, đăng ký tài khoản Stipe (nếu bạn chưa làm như vậy) và điều hướng đến trang tổng quan . Nhấp vào "Nhà phát triển" và sau đó từ danh sách ở thanh bên trái, nhấp vào "Khóa API":

Khóa nhà phát triển sọc

Mỗi tài khoản Stripe có bốn khóa API : hai khóa để thử nghiệm và hai khóa để sản xuất. Mỗi cặp có một "khóa bí mật" và một "khóa có thể xuất bản". Không tiết lộ chìa khóa bí mật cho bất kỳ ai; khóa có thể xuất bản sẽ được nhúng vào JavaScript trên trang mà bất kỳ ai cũng có thể nhìn thấy.

Hiện tại, nút chuyển đổi cho "Xem dữ liệu kiểm tra" ở phía trên bên phải cho biết rằng chúng tôi đang sử dụng các khóa kiểm tra ngay bây giờ. Đó là những gì chúng tôi muốn.

Ở cuối tệp settings.py của bạn , thêm hai dòng sau bao gồm khóa bí mật kiểm tra và khóa có thể xuất bản của riêng bạn. Đảm bảo bao gồm các ''ký tự xung quanh các phím thực tế.

# djangostripe/settings.py

STRIPE_PUBLISHABLE_KEY = '<enter your stripe publishable key>'
STRIPE_SECRET_KEY = '<enter your stripe secret key>'

Cuối cùng, bạn sẽ cần chỉ định "Tên tài khoản" trong "Cài đặt tài khoản" tại https://dashboard.stripe.com/settings/account .

Tạo một sản phẩm

Tiếp theo, hãy tạo một sản phẩm đăng ký để bán.

Nhấp vào "Sản phẩm" và sau đó "Thêm sản phẩm".

Thêm tên và mô tả sản phẩm, nhập giá và chọn "Định kỳ":

Sọc Thêm sản phẩm

Nhấp vào "Lưu sản phẩm".

Tiếp theo, lấy ID API của giá:

Sọc Thêm sản phẩm

Lưu ID trong tệp settings.py như sau:

# djangostripe/settings.py

STRIPE_PRICE_ID = '<enter your stripe price id>'

Xác thực

Để liên kết người dùng Django với khách hàng của Stripe và triển khai quản lý đăng ký trong tương lai, chúng tôi cần thực thi xác thực người dùng trước khi cho phép khách hàng đăng ký dịch vụ. Chúng tôi có thể đạt được điều này bằng cách thêm trình @login_requiredtrang trí vào tất cả các chế độ xem yêu cầu xác thực.

Đầu tiên chúng ta hãy bảo vệ homequan điểm:

# subscriptions/views.py

from django.contrib.auth.decorators import login_required  # new
from django.shortcuts import render

@login_required  # new
def home(request):
    return render(request, 'home.html')

Bây giờ, khi người dùng không được xác thực cố gắng truy cập homechế độ xem, họ sẽ được chuyển hướng đến chế độ đã LOGIN_REDIRECT_URLxác định trong settings.py .

Nếu bạn có hệ thống xác thực ưa thích, hãy thiết lập hệ thống đó ngay bây giờ và định cấu hình LOGIN_REDIRECT_URL, nếu không, hãy chuyển sang phần tiếp theo để cài đặt django-allauth .

django-allauth (Tùy chọn)

django-allauth là một trong những gói Django phổ biến nhất để xử lý xác thực, đăng ký, quản lý tài khoản và xác thực tài khoản của bên thứ ba. Chúng tôi sẽ sử dụng nó để cấu hình một hệ thống đăng ký / đăng nhập đơn giản.

Đầu tiên, hãy cài đặt gói:

(env)$ pip install django-allauth

Cập nhật INSTALLED_APPStrong djangostripe / settings.py như vậy:

# djangostripe/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.sites', # new
    'allauth', # new
    'allauth.account', # new
    'allauth.socialaccount', # new
    'subscriptions.apps.SubscriptionsConfig',
]

Tiếp theo, thêm cấu hình django-allauth sau vào djangostripe / settings.py :

# djangostripe/settings.py

AUTHENTICATION_BACKENDS = [
    # Needed to login by username in Django admin, regardless of `allauth`
    'django.contrib.auth.backends.ModelBackend',

    # `allauth` specific authentication methods, such as login by e-mail
    'allauth.account.auth_backends.AuthenticationBackend',
]

# We have to set this variable, because we enabled 'django.contrib.sites'
SITE_ID = 1

# User will be redirected to this page after logging in
LOGIN_REDIRECT_URL = '/'

# If you don't have an email server running yet add this line to avoid any possible errors.
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

Đăng ký các URL ủy quyền:

# djangostripe/urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('subscriptions.urls')),
    path('accounts/', include('allauth.urls')),  # new
]

Áp dụng các di chuyển:

(env)$ python manage.py migrate

Kiểm tra xác thực bằng cách chạy máy chủ và điều hướng đến http://localhost.com:8000/ . Bạn sẽ được chuyển hướng đến trang đăng ký. Tạo một tài khoản và sau đó đăng nhập.

Mô hình cơ sở dữ liệu

Để xử lý khách hàng và đăng ký một cách chính xác, chúng tôi cần lưu trữ một số thông tin trong cơ sở dữ liệu của mình. Hãy tạo một mô hình mới được gọi là mô hình StripeCustomernày sẽ lưu trữ Stripe customerIdsubscriptionIdliên kết nó trở lại với người dùng xác thực Django. Điều này sẽ cho phép chúng tôi tìm nạp dữ liệu đăng ký và khách hàng của mình từ Stripe.

Về mặt lý thuyết, chúng tôi có thể lấy customerIdsubscriptionIdtừ Stripe bất cứ khi nào chúng tôi cần chúng, nhưng điều đó sẽ làm tăng đáng kể cơ hội nhận được tỷ lệ bị giới hạn bởi Stripe.

Hãy tạo mô hình của chúng tôi bên trong subscribe / models.py :

# subscriptions/models.py

from django.contrib.auth.models import User
from django.db import models


class StripeCustomer(models.Model):
    user = models.OneToOneField(to=User, on_delete=models.CASCADE)
    stripeCustomerId = models.CharField(max_length=255)
    stripeSubscriptionId = models.CharField(max_length=255)

    def __str__(self):
        return self.user.username

Đăng ký nó với quản trị viên trong subscribe / admin.py :

# subscriptions/admin.py

from django.contrib import admin
from subscriptions.models import StripeCustomer


admin.site.register(StripeCustomer)

Tạo và áp dụng các di chuyển:

(env)$ python manage.py makemigrations && python manage.py migrate

Nhận khóa dành cho nhà xuất bản

Tệp tĩnh JavaScript

Bắt đầu bằng cách tạo một tệp tĩnh mới để chứa tất cả JavaScript của chúng tôi:

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

Thêm kiểm tra nhanh vào tệp main.js mới :

// static/main.js

console.log("Sanity check!");

Sau đó, cập nhật tệp settings.py để Django biết nơi tìm tệp tĩnh:

# djangostripe/settings.py

STATIC_URL = 'static/'

# for django >= 3.1
STATICFILES_DIRS = [Path(BASE_DIR).joinpath('static')]  # new

# for django < 3.1
# STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]  # new

Thêm thẻ mẫu tĩnh cùng với thẻ tập lệnh mới bên trong mẫu HTML:

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

{% load static %} <!-- new -->

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

Chạy lại máy chủ phát triển. Điều hướng đến http: // localhost: 8000 / và mở bảng điều khiển JavaScript. Bạn sẽ thấy kiểm tra sự tỉnh táo bên trong bảng điều khiển của mình.

Lượt xem

Tiếp theo, thêm một chế độ xem mới vào subscribe / views.py để xử lý yêu cầu AJAX:

# subscriptions/views.py

from django.conf import settings # new
from django.contrib.auth.decorators import login_required
from django.http.response import JsonResponse # new
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt # new


@login_required
def home(request):
    return render(request, 'home.html')


# new
@csrf_exempt
def stripe_config(request):
    if request.method == 'GET':
        stripe_config = {'publicKey': settings.STRIPE_PUBLISHABLE_KEY}
        return JsonResponse(stripe_config, safe=False)

Thêm một URL mới:

# subscriptions/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='subscriptions-home'),
    path('config/', views.stripe_config),  # new
]

Yêu cầu AJAX

Tiếp theo, sử dụng API Tìm nạp để thực hiện yêu cầu AJAX tới /config/điểm cuối mới trong static / main.js :

// 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);
});

Phản hồi từ một fetchyêu cầu là một luồng có thể đọc được . result.json()trả về một lời hứa, mà chúng tôi đã giải quyết cho một đối tượng JavaScript - tức là data,. Sau đó, chúng tôi sử dụng ký hiệu dấu chấm để truy cập publicKeynhằm lấy khóa có thể xuất bản.

Tạo phiên thanh toán

Tiếp tục, chúng ta cần đính kèm một trình xử lý sự kiện vào sự kiện nhấp chuột của nút, điều này sẽ gửi một yêu cầu AJAX khác đến máy chủ để tạo ID phiên Checkout mới.

Lượt xem

Đầu tiên, thêm chế độ xem mới:

# subscriptions/views.py

@csrf_exempt
def create_checkout_session(request):
    if request.method == 'GET':
        domain_url = 'http://localhost:8000/'
        stripe.api_key = settings.STRIPE_SECRET_KEY
        try:
            checkout_session = stripe.checkout.Session.create(
                client_reference_id=request.user.id if request.user.is_authenticated else None,
                success_url=domain_url + 'success?session_id={CHECKOUT_SESSION_ID}',
                cancel_url=domain_url + 'cancel/',
                payment_method_types=['card'],
                mode='subscription',
                line_items=[
                    {
                        'price': settings.STRIPE_PRICE_ID,
                        'quantity': 1,
                    }
                ]
            )
            return JsonResponse({'sessionId': checkout_session['id']})
        except Exception as e:
            return JsonResponse({'error': str(e)})

Ở đây, nếu phương thức yêu cầu là GET, chúng tôi đã xác định a domain_url, gán khóa bí mật Stripe cho stripe.api_key(vì vậy nó sẽ được gửi tự động khi chúng tôi thực hiện yêu cầu tạo Phiên kiểm tra mới), tạo Phiên kiểm tra và gửi lại ID trong phản ứng. Hãy lưu ý success_urlcancel_url. Người dùng sẽ được chuyển hướng trở lại các URL đó trong trường hợp thanh toán thành công hoặc hủy tương ứng. Chúng tôi sẽ sớm thiết lập các chế độ xem đó.

Đừng quên nhập:

import stripe

Toàn bộ tệp bây giờ sẽ trông như thế này:

# subscriptions/views.py

import stripe
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.http.response import JsonResponse
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt


@login_required
def home(request):
    return render(request, 'home.html')


@csrf_exempt
def stripe_config(request):
    if request.method == 'GET':
        stripe_config = {'publicKey': settings.STRIPE_PUBLISHABLE_KEY}
        return JsonResponse(stripe_config, safe=False)


@csrf_exempt
def create_checkout_session(request):
    if request.method == 'GET':
        domain_url = 'http://localhost:8000/'
        stripe.api_key = settings.STRIPE_SECRET_KEY
        try:
            checkout_session = stripe.checkout.Session.create(
                client_reference_id=request.user.id if request.user.is_authenticated else None,
                success_url=domain_url + 'success?session_id={CHECKOUT_SESSION_ID}',
                cancel_url=domain_url + 'cancel/',
                payment_method_types=['card'],
                mode='subscription',
                line_items=[
                    {
                        'price': settings.STRIPE_PRICE_ID,
                        'quantity': 1,
                    }
                ]
            )
            return JsonResponse({'sessionId': checkout_session['id']})
        except Exception as e:
            return JsonResponse({'error': str(e)})

Yêu cầu AJAX

Đăng ký URL phiên thanh toán:

# subscriptions/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='subscriptions-home'),
    path('config/', views.stripe_config),
    path('create-checkout-session/', views.create_checkout_session),  # new
]

Thêm trình xử lý sự kiện và yêu cầu AJAX tiếp theo vào 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
  let submitBtn = document.querySelector("#submitBtn");
  if (submitBtn !== null) {
    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);
      });
    });
  }
});

Ở đây, sau khi giải quyết result.json()lời hứa, chúng tôi đã gọi redirectToCheckout với ID phiên Checkout từ lời hứa đã giải quyết.

Điều hướng đến http: // localhost: 8000 / . Khi nhấp vào nút, bạn sẽ được chuyển hướng đến một phiên bản của Stripe Checkout (một trang được lưu trữ trên Stripe để thu thập thông tin thanh toán một cách an toàn) với thông tin đăng ký:

Kiểm tra sọc

Chúng tôi có thể kiểm tra biểu mẫu bằng cách sử dụng một trong một số số thẻ kiểm tra mà Stripe cung cấp. Hãy sử dụng 4242 4242 4242 4242. Đảm bảo ngày hết hạn trong tương lai. Thêm 3 số bất kỳ cho CVC và 5 số bất kỳ cho mã bưu điện. Nhập bất kỳ địa chỉ email và tên. Nếu mọi việc suôn sẻ, thanh toán sẽ được xử lý và bạn sẽ được đăng ký, nhưng chuyển hướng sẽ không thành công vì chúng tôi chưa thiết lập /success/URL.

Chuyển hướng người dùng

Tiếp theo, chúng tôi sẽ tạo thành công và hủy lượt xem và chuyển hướng người dùng đến trang thích hợp sau khi thanh toán.

Lượt xem:

# subscriptions/views.py

@login_required
def success(request):
    return render(request, 'success.html')


@login_required
def cancel(request):
    return render(request, 'cancel.html')

Tạo các mẫu thành công.htmlhủy bỏ.html .

Thành công:

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

{% load static %}

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Django + Stripe Subscriptions</title>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
  </head>
  <body>
    <div class="container mt-5">
      <p>You have successfully subscribed!</p>
      <p><a href="{% url "subscriptions-home" %}">Return to the dashboard</a></p>
    </div>
  </body>
</html>

Hủy bỏ:

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

{% load static %}

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Django + Stripe Subscriptions</title>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
  </head>
  <body>
    <div class="container mt-5">
      <p>You have cancelled the checkout.</p>
      <p><a href="{% url "subscriptions-home" %}">Return to the dashboard</a></p>
    </div>
  </body>
</html>

Đăng ký các chế độ xem mới bên trong subscribe / urls.py :

# subscriptions/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='subscriptions-home'),
    path('config/', views.stripe_config),
    path('create-checkout-session/', views.create_checkout_session),
    path('success/', views.success),  # new
    path('cancel/', views.cancel),  # new
]

The user should now be redirected to /success if the payment goes through and cancel/ if the payment fails. Test this out.

Stripe Webhooks

Our app works well at this point, but we still can't programmatically confirm payments. We also haven't added a new customer to the StripeCustomer model when a customer subscribes successfully. We already redirect the user to the success page after they check out, but we can't rely on that page alone (as confirmation) since payment confirmation happens asynchronously.

There are two types of events in Stripe and programming in general. Synchronous events, which have an immediate effect and results (e.g., creating a customer), and asynchronous events, which don't have an immediate result (e.g., confirming payments). Because payment confirmation is done asynchronously, the user might get redirected to the success page before their payment is confirmed and before we receive their funds.

One of the easiest ways to get notified when the payment goes through is to use a callback or so-called Stripe webhook. We'll need to create a simple endpoint in our application, which Stripe will call whenever an event occurs (e.g., when a user subscribes). By using webhooks, we can be absolutely sure that the payment went through successfully.

In order to use webhooks, we need to:

  1. Set up the webhook endpoint
  2. Test the endpoint using the Stripe CLI
  3. Register the endpoint with Stripe

Endpoint

Create a new view called stripe_webhook which will create a new StripeCustomer every time someone subscribes to our service:

# subscriptions/views.py

@csrf_exempt
def stripe_webhook(request):
    stripe.api_key = settings.STRIPE_SECRET_KEY
    endpoint_secret = settings.STRIPE_ENDPOINT_SECRET
    payload = request.body
    sig_header = request.META['HTTP_STRIPE_SIGNATURE']
    event = None

    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, endpoint_secret
        )
    except ValueError as e:
        # Invalid payload
        return HttpResponse(status=400)
    except stripe.error.SignatureVerificationError as e:
        # Invalid signature
        return HttpResponse(status=400)

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

        # Fetch all the required data from session
        client_reference_id = session.get('client_reference_id')
        stripe_customer_id = session.get('customer')
        stripe_subscription_id = session.get('subscription')

        # Get the user and create a new StripeCustomer
        user = User.objects.get(id=client_reference_id)
        StripeCustomer.objects.create(
            user=user,
            stripeCustomerId=stripe_customer_id,
            stripeSubscriptionId=stripe_subscription_id,
        )
        print(user.username + ' just subscribed.')

    return HttpResponse(status=200)

stripe_webhook now serves as our webhook endpoint. Here, we're only looking for checkout.session.completed events which are called whenever a checkout is successful, but you can use the same pattern for other Stripe events.

Make the following changes to the imports:

# subscriptions/views.py

import stripe
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User  # new
from django.http.response import JsonResponse, HttpResponse  # updated
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt

from subscriptions.models import StripeCustomer  # new

The only thing left do do to make the endpoint accessible is to register it in urls.py:

# subscriptions/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.home, name='subscriptions-home'),
    path('config/', views.stripe_config),
    path('create-checkout-session/', views.create_checkout_session),
    path('success/', views.success),
    path('cancel/', views.cancel),
    path('webhook/', views.stripe_webhook),  # new
]

Testing the webhook

We'll use the Stripe CLI to test the webhook.

Once downloaded and installed, run the following command in a new terminal window to log in to your Stripe account:

$ stripe login

This command should generate a pairing code:

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)

By pressing Enter, the CLI will open your default web browser and ask for permission to access your account information. Go ahead and allow access. Back in your terminal, you should see something similar to:

> Done! The Stripe CLI is configured for Django 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.

Next, we can start listening to Stripe events and forward them to our endpoint using the following command:

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

This will also generate a webhook signing secret:

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

In order to initialize the endpoint, add the secret to the settings.py file:

# djangostribe/settings.py

STRIPE_ENDPOINT_SECRET = '<your webhook signing secret here>'

Stripe will now forward events to our endpoint. To test, run another test payment through with 4242 4242 4242 4242. In your terminal, you should see the <USERNAME> just subscribed. message.

Once done, stop the stripe listen --forward-to localhost:8000/webhook/ process.

Register the endpoint

Finally, after deploying your app, you can register the endpoint in the Stripe dashboard, under Developers > Webhooks. This will generate a webhook signing secret for use in your production app.

For example:

Django webhook

Fetch Subscription Data

Our app now allows users to subscribe to our service, but we still have no way to fetch their subscription data and display it.

Update the home view:

# subscriptions/views.py

@login_required
def home(request):
    try:
        # Retrieve the subscription & product
        stripe_customer = StripeCustomer.objects.get(user=request.user)
        stripe.api_key = settings.STRIPE_SECRET_KEY
        subscription = stripe.Subscription.retrieve(stripe_customer.stripeSubscriptionId)
        product = stripe.Product.retrieve(subscription.plan.product)

        # Feel free to fetch any additional data from 'subscription' or 'product'
        # https://stripe.com/docs/api/subscriptions/object
        # https://stripe.com/docs/api/products/object

        return render(request, 'home.html', {
            'subscription': subscription,
            'product': product,
        })

    except StripeCustomer.DoesNotExist:
        return render(request, 'home.html')

Here, if a StripeCustomer exists, we use the subscriptionId to fetch the customer's subscription and product info from the Stripe API.

Modify the home.html template to display the current plan to subscribed users:

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

{% load static %}

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Django + Stripe Subscriptions</title>
    <script src="https://js.stripe.com/v3/"></script>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
    <script src="{% static 'main.js' %}"></script>
  </head>
  <body>
    <div class="container mt-5">
      {% if 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>

Khách hàng đã đăng ký của chúng tôi giờ đây sẽ thấy gói đăng ký hiện tại của họ, trong khi những người khác vẫn sẽ thấy nút đăng ký:

Chế độ xem đã Đăng ký

Hạn chế quyền truy cập của người dùng

Nếu bạn muốn hạn chế quyền truy cập vào các chế độ xem cụ thể đối với chỉ những người dùng đã đăng ký, bạn có thể tìm nạp đăng ký như chúng tôi đã làm trong bước trước và kiểm tra xem có subscription.status == "active". Bằng cách thực hiện kiểm tra này, bạn sẽ đảm bảo đăng ký vẫn hoạt động, có nghĩa là đăng ký đã được thanh toán và chưa bị hủy.

Các trạng thái đăng ký có thể có incompletekhác là ,,,, hoặc .incomplete_expiredtrialingactivepast_duecanceledunpaid

Sự kết luận

Chúng tôi đã tạo thành công một ứng dụng web Django cho phép người dùng đăng ký dịch vụ của chúng tôi và xem gói của họ. Khách hàng của chúng tôi cũng sẽ được tự động lập hóa đơn hàng tháng.

Đây chỉ là những điều cơ bản. Bạn vẫn cần:

  • Cho phép người dùng quản lý / hủy gói hiện tại của họ
  • Xử lý các lỗi thanh toán trong tương lai

Bạn cũng sẽ muốn sử dụng các biến môi trường cho domain_urlkhóa API và bí mật ký webhook thay vì mã hóa cứng chúng.

Lấy mã từ repo django-sọc-đăng ký trên GitHub.

Nguồn:  https://testdriven.io

#django #python #stripe 

Ahebwe  Oscar

Ahebwe Oscar

1620177818

Django admin full Customization step by step

Welcome to my blog , hey everyone in this article you learn how to customize the Django app and view in the article you will know how to register  and unregister  models from the admin view how to add filtering how to add a custom input field, and a button that triggers an action on all objects and even how to change the look of your app and page using the Django suit package let’s get started.

Database

Custom Titles of Django Admin

Exclude in Django Admin

Fields in Django Admin

#django #create super user django #customize django admin dashboard #django admin #django admin custom field display #django admin customization #django admin full customization #django admin interface #django admin register all models #django customization

Ahebwe  Oscar

Ahebwe Oscar

1620185280

How model queries work in Django

How model queries work in Django

Welcome to my blog, hey everyone in this article we are going to be working with queries in Django so for any web app that you build your going to want to write a query so you can retrieve information from your database so in this article I’ll be showing you all the different ways that you can write queries and it should cover about 90% of the cases that you’ll have when you’re writing your code the other 10% depend on your specific use case you may have to get more complicated but for the most part what I cover in this article should be able to help you so let’s start with the model that I have I’ve already created it.

**Read More : **How to make Chatbot in Python.

Read More : Django Admin Full Customization step by step

let’s just get into this diagram that I made so in here:

django queries aboutDescribe each parameter in Django querset

we’re making a simple query for the myModel table so we want to pull out all the information in the database so we have this variable which is gonna hold a return value and we have our myModel models so this is simply the myModel model name so whatever you named your model just make sure you specify that and we’re gonna access the objects attribute once we get that object’s attribute we can simply use the all method and this will return all the information in the database so we’re gonna start with all and then we will go into getting single items filtering that data and go to our command prompt.

Here and we’ll actually start making our queries from here to do this let’s just go ahead and run** Python manage.py shell** and I am in my project file so make sure you’re in there when you start and what this does is it gives us an interactive shell to actually start working with our data so this is a lot like the Python shell but because we did manage.py it allows us to do things a Django way and actually query our database now open up the command prompt and let’s go ahead and start making our first queries.

#django #django model queries #django orm #django queries #django query #model django query #model query #query with django

Hoang Tran

Hoang Tran

1660393740

Định Cấu Hình Trang Web Django Để Chấp Nhận Thanh Toán Với Stripe

Trong hướng dẫn này, tôi sẽ trình bày cách định cấu hình trang web Django mới từ đầu để chấp nhận thanh toán một lần bằng Stripe .

Tùy chọn thanh toán bằng sọc

Hiện có ba cách để chấp nhận thanh toán một lần với Stripe:

  1. API tính phí (kế thừa)
  2. Stripe Checkout (trọng tâm của hướng dẫn này)
  3. API ý định thanh toán (thường được kết hợp với Phần tử sọc)

Bạn nên sử dụng cái nào?

  1. Sử dụng Checkout nếu bạn muốn thiết lập và chạy nhanh. Nếu bạn đã quen với phiên bản Checkout theo phương thức cũ , thì đây là cách để thực hiện. Nó cung cấp rất nhiều tính năng sẵn có, hỗ trợ nhiều ngôn ngữ và bao gồm một con đường dễ dàng để thực hiện các khoản thanh toán định kỳ . Quan trọng nhất, Checkout quản lý toàn bộ quy trình thanh toán cho bạn, vì vậy bạn có thể bắt đầu chấp nhận thanh toán mà thậm chí không cần phải thêm một biểu mẫu nào!
  2. Sử dụng API ý định thanh toán Nếu bạn muốn có trải nghiệm tùy chỉnh hơn cho người dùng cuối của mình.

Mặc dù bạn vẫn có thể sử dụng API tính phí, nhưng nếu bạn mới sử dụng Stripe, đừng sử dụng nó vì nó không hỗ trợ các quy định ngân hàng mới nhất (như SCA ). Bạn sẽ thấy tỷ lệ từ chối cao. Để biết thêm thông tin, hãy xem lại trang API ý định thanh toán so với khoản phí từ tài liệu chính thức của Stripe.

Bạn vẫn sử dụng API tính phí? Nếu hầu hết khách hàng của bạn sống ở Hoa Kỳ hoặc Canada, bạn chưa cần phải di cư. Xem lại hướng dẫn hướng dẫn di chuyển Checkout để biết thêm thông tin.

Thiết lập dự án

Tạo một thư mục dự án mới cùng với một dự án Django mới có tên djangostripe:

$ mkdir django-stripe-checkout && cd django-stripe-checkout
$ python3.10 -m venv env
$ source env/bin/activate

(env)$ pip install django==3.2.9
(env)$ django-admin startproject djangostripe .

Hãy trao đổi virtualenv và Pip cho thơ hoặc Pipenv . Để biết thêm, hãy xem lại Môi trường Python hiện đại .

Tiếp theo, tạo một ứng dụng mới có tên payments:

(env)$ python manage.py startapp payments

Bây giờ, hãy thêm ứng dụng mới vào INSTALLED_APPScấu hình trong settings.py :

# djangostripe/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    # Local
    'payments.apps.PaymentsConfig', # new
]

Cập nhật tệp urls.py cấp dự án với paymentsứng dụng:

# djangostripe/urls.py

from django.contrib import admin
from django.urls import path, include # new

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('payments.urls')), # new
]

Cũng tạo tệp urls.py trong ứng dụng mới:

(env)$ touch payments/urls.py

Sau đó điền nó như sau:

# payments/urls.py

from django.urls import path

from . import views

urlpatterns = [
    path('', views.HomePageView.as_view(), name='home'),
]

Bây giờ hãy thêm tệp views.py :

# payments/views.py

from django.views.generic.base import TemplateView

class HomePageView(TemplateView):
    template_name = 'home.html'

Và tạo một thư mục và tệp "mẫu" dành riêng cho trang chủ của chúng tôi.

(env)$ mkdir templates
(env)$ touch templates/home.html

Sau đó, thêm HTML sau:

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

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Django + Stripe Checkout</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
    <script defer src="https://use.fontawesome.com/releases/v5.15.4/js/all.js"></script>
  </head>
  <body>
  <section class="section">
    <div class="container">
      <button class="button is-primary" id="submitBtn">Purchase!</button>
    </div>
  </section>
  </body>
</html>

Đảm bảo cập nhật tệp settings.py để Django biết tìm thư mục "mẫu":

# djangostripe/settings.py

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': ['templates'], # new
        ...

Cuối cùng chạy migrateđể đồng bộ cơ sở dữ liệu và runserverkhởi động máy chủ web cục bộ của Django.

(env)$ python manage.py migrate
(env)$ python manage.py runserver

That's it! Check out http://localhost:8000/ and you'll see the homepage:

Django

Add Stripe

Time for Stripe. Start by installing it:

(env)$ pip install stripe==2.63.0

Next, register for a Stripe account (if you haven't already done so) and navigate to the dashboard. Click on "Developers":

Nhà phát triển Stripe

Then in the left sidebar click on "API keys":

Khóa nhà phát triển sọc

Each Stripe account has four API keys: two for testing and two for production. Each pair has a "secret key" and a "publishable key". Do not reveal the secret key to anyone; the publishable key will be embedded in the JavaScript on the page that anyone can see.

Hiện tại, nút chuyển đổi cho "Xem dữ liệu kiểm tra" ở phía trên bên phải cho biết rằng chúng tôi đang sử dụng các khóa kiểm tra ngay bây giờ. Đó là những gì chúng tôi muốn.

Ở cuối tệp settings.py của bạn , thêm hai dòng sau bao gồm bí mật kiểm tra của riêng bạn và kiểm tra khóa có thể xuất bản. Đảm bảo bao gồm các ''ký tự xung quanh các phím thực tế.

# djangostripe/settings.py

STRIPE_PUBLISHABLE_KEY = '<your test publishable key here>'
STRIPE_SECRET_KEY = '<your test secret key here>'

Cuối cùng, bạn sẽ cần chỉ định "Tên tài khoản" trong "Cài đặt tài khoản" của mình tại https://dashboard.stripe.com/settings/account :

Tên tài khoản sọc

Tạo một sản phẩm

Tiếp theo, chúng ta cần tạo ra một sản phẩm để bán.

Nhấp vào "Sản phẩm" và sau đó "Thêm sản phẩm":

Sọc Thêm sản phẩm

Thêm tên sản phẩm, nhập giá và chọn "Một lần":

Sọc Thêm sản phẩm

Nhấp vào "Lưu sản phẩm".

Luồng người dùng

Sau khi người dùng nhấp vào nút mua hàng, chúng ta cần thực hiện các thao tác sau:

Get Publishable Key

  • Send an XHR request from the client to the server requesting the publishable key
  • Respond with the key
  • Use the key to create a new instance of Stripe.js

Create Checkout Session

  • Send another XHR request to the server requesting a new Checkout Session ID
  • Generate a new Checkout Session and send back the ID
  • Redirect to the checkout page for the user to finish their purchase

Redirect the User Appropriately

  • Redirect to a success page after a successful payment
  • Redirect to a cancellation page after a cancelled payment

Confirm Payment with Stripe Webhooks

  • Set up the webhook endpoint
  • Test the endpoint using the Stripe CLI
  • Register the endpoint with Stripe

Get Publishable Key

JavaScript Static File

Let's start by creating a new static file to hold all of our JavaScript:

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

Add a quick sanity check to the new main.js file:

// static/main.js

console.log("Sanity check!");

Sau đó, cập nhật tệp settings.py để Django biết nơi tìm tệp tĩnh:

# djangostripe/settings.py

STATIC_URL = '/static/'

# for django >= 3.1
STATICFILES_DIRS = [BASE_DIR / 'static']  # new

# for django < 3.1
# STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]  # new

Thêm thẻ mẫu tĩnh cùng với thẻ tập lệnh mới bên trong mẫu HTML:

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

{% load static %} <!-- new -->

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Django + Stripe Checkout</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
    <script src="{% static 'main.js' %}"></script>   <!-- new -->
    <script defer src="https://use.fontawesome.com/releases/v5.15.4/js/all.js"></script>
  </head>
  <body>
  <section class="section">
    <div class="container">
      <button class="button is-primary" id="submitBtn">Purchase!</button>
    </div>
  </section>
  </body>
</html>

Chạy lại máy chủ phát triển. Điều hướng đến http: // localhost: 8000 / và mở bảng điều khiển JavaScript. Bạn sẽ thấy kiểm tra sự tỉnh táo:

Kiểm tra tình trạng JavaScript

Lượt xem

Tiếp theo, thêm một chế độ xem mới vào Payment / views.py để xử lý yêu cầu XHR:

# payments/views.py

from django.conf import settings # new
from django.http.response import JsonResponse # new
from django.views.decorators.csrf import csrf_exempt # new
from django.views.generic.base import TemplateView


class HomePageView(TemplateView):
    template_name = 'home.html'


# new
@csrf_exempt
def stripe_config(request):
    if request.method == 'GET':
        stripe_config = {'publicKey': settings.STRIPE_PUBLISHABLE_KEY}
        return JsonResponse(stripe_config, safe=False)

Thêm cả một URL:

# payments/urls.py

from django.urls import path

from . import views

urlpatterns = [
    path('', views.HomePageView.as_view(), name='home'),
    path('config/', views.stripe_config),  # new
]

Yêu cầu XHR

Tiếp theo, sử dụng API Tìm nạp để thực hiện một yêu cầu XHR (XMLHttpRequest) tới /config/điểm cuối mới trong static / main.js :

// 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);
});

Phản hồi từ một fetchyêu cầu là một luồng có thể đọc được . result.json()trả về một lời hứa, mà chúng tôi đã giải quyết cho một đối tượng JavaScript - ví dụ data:. Sau đó, chúng tôi sử dụng ký hiệu dấu chấm để truy cập publicKeynhằm lấy khóa có thể xuất bản.

Bao gồm Stripe.js trong các mẫu / home.html như sau:

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

{% load static %}

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Django + Stripe Checkout</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
    <script src="https://js.stripe.com/v3/"></script>  <!-- new -->
    <script src="{% static 'main.js' %}"></script>
    <script defer src="https://use.fontawesome.com/releases/v5.15.4/js/all.js"></script>
  </head>
  <body>
  <section class="section">
    <div class="container">
      <button class="button is-primary" id="submitBtn">Purchase!</button>
    </div>
  </section>
  </body>
</html>

Bây giờ, sau khi tải trang, một lệnh gọi sẽ được thực hiện tới /config/, lệnh này sẽ phản hồi bằng khóa có thể xuất bản Stripe. Sau đó, chúng tôi sẽ sử dụng khóa này để tạo một phiên bản mới của Stripe.js.

Lưu lượng:

Nhận khóa dành cho nhà xuất bản

  • Gửi yêu cầu XHR từ máy khách đến máy chủ yêu cầu khóa có thể xuất bản
  • Phản hồi bằng phím
  • Sử dụng khóa để tạo một phiên bản mới của Stripe.js

Tạo phiên thanh toán

  • Gửi một yêu cầu XHR khác đến máy chủ yêu cầu ID phiên Checkout mới
  • Tạo Phiên thanh toán mới và gửi lại ID
  • Chuyển hướng đến trang thanh toán để người dùng hoàn tất việc mua hàng của họ

Chuyển hướng người dùng một cách thích hợp

  • Chuyển hướng đến trang thành công sau khi thanh toán thành công
  • Chuyển hướng đến trang hủy sau khi thanh toán bị hủy

Xác nhận thanh toán bằng Stripe Webhooks

  • Thiết lập điểm cuối webhook
  • Kiểm tra điểm cuối bằng Stripe CLI
  • Đăng ký điểm cuối với Stripe

Tạo phiên thanh toán

Tiếp tục, chúng ta cần đính kèm một trình xử lý sự kiện vào sự kiện nhấp chuột của nút, điều này sẽ gửi một yêu cầu XHR khác đến máy chủ để tạo ID phiên Checkout mới.

Lượt xem

Đầu tiên, hãy thêm chế độ xem mới:

# payments/views.py

@csrf_exempt
def create_checkout_session(request):
    if request.method == 'GET':
        domain_url = 'http://localhost:8000/'
        stripe.api_key = settings.STRIPE_SECRET_KEY
        try:
            # Create new Checkout Session for the order
            # Other optional params include:
            # [billing_address_collection] - to display billing address details on the page
            # [customer] - if you have an existing Stripe Customer ID
            # [payment_intent_data] - capture the payment later
            # [customer_email] - prefill the email input in the form
            # For full details see https://stripe.com/docs/api/checkout/sessions/create

            # ?session_id={CHECKOUT_SESSION_ID} means the redirect will have the session ID set as a query param
            checkout_session = stripe.checkout.Session.create(
                success_url=domain_url + 'success?session_id={CHECKOUT_SESSION_ID}',
                cancel_url=domain_url + 'cancelled/',
                payment_method_types=['card'],
                mode='payment',
                line_items=[
                    {
                        'name': 'T-shirt',
                        'quantity': 1,
                        'currency': 'usd',
                        'amount': '2000',
                    }
                ]
            )
            return JsonResponse({'sessionId': checkout_session['id']})
        except Exception as e:
            return JsonResponse({'error': str(e)})

Ở đây, nếu phương thức yêu cầu là GET, chúng tôi đã xác định a domain_url, gán khóa bí mật Stripe cho stripe.api_key(vì vậy nó sẽ được gửi tự động khi chúng tôi thực hiện yêu cầu tạo Phiên kiểm tra mới), tạo Phiên kiểm tra và gửi lại ID trong phản ứng. Hãy lưu ý success_urlcancel_url. Người dùng sẽ được chuyển hướng trở lại các URL đó trong trường hợp thanh toán thành công hoặc hủy tương ứng. Chúng tôi sẽ sớm thiết lập các chế độ xem đó.

Đừng quên nhập:

import stripe

Thêm URL:

# payments/urls.py

from django.urls import path

from . import views

urlpatterns = [
    path('', views.HomePageView.as_view(), name='home'),
    path('config/', views.stripe_config),
    path('create-checkout-session/', views.create_checkout_session), # new
]

Yêu cầu XHR

Thêm trình xử lý sự kiện và yêu cầu XHR tiếp theo vào 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);
    });
  });
});

Ở đây, sau khi giải quyết result.json()lời hứa, chúng tôi đã gọi redirectToCheckout với ID phiên Checkout từ lời hứa đã giải quyết.

Navigate to http://localhost:8000/. On button click you should be redirected to an instance of Stripe Checkout (a Stripe-hosted page to securely collect payment information) with the T-shirt product information:

Kiểm tra sọc

We can test the form by using one of the several test card numbers that Stripe provides. Let's use 4242 4242 4242 4242. Make sure the expiration date is in the future. Add any 3 numbers for the CVC and any 5 numbers for the postal code. Enter any email address and name. If all goes well, the payment should be processed, but the redirect will fail since we have not set up the /success/ URL yet.

Flow:

Get Publishable Key

  • Send an XHR request from the client to the server requesting the publishable key
  • Respond with the key
  • Use the key to create a new instance of Stripe.js

Tạo phiên thanh toán

  • Gửi một yêu cầu XHR khác đến máy chủ yêu cầu ID phiên Checkout mới
  • Tạo Phiên thanh toán mới và gửi lại ID
  • Chuyển hướng đến trang thanh toán để người dùng hoàn tất việc mua hàng của họ

Chuyển hướng người dùng một cách thích hợp

  • Chuyển hướng đến trang thành công sau khi thanh toán thành công
  • Chuyển hướng đến trang hủy sau khi thanh toán bị hủy

Xác nhận thanh toán bằng Stripe Webhooks

  • Thiết lập điểm cuối webhook
  • Kiểm tra điểm cuối bằng Stripe CLI
  • Đăng ký điểm cuối với Stripe

Chuyển hướng người dùng một cách thích hợp

Cuối cùng, hãy sắp xếp các mẫu, chế độ xem và URL để xử lý chuyển hướng thành công và hủy.

Mẫu thành công:

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

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Django + Stripe Checkout</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
    <script defer src="https://use.fontawesome.com/releases/v5.15.4/js/all.js"></script>
  </head>
  <body>
  <section class="section">
    <div class="container">
      <p>Your payment succeeded.</p>
    </div>
  </section>
  </body>
</html>

Mẫu đã hủy:

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

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Django + Stripe Checkout</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
    <script defer src="https://use.fontawesome.com/releases/v5.15.4/js/all.js"></script>
  </head>
  <body>
  <section class="section">
    <div class="container">
      <p>Your payment was cancelled.</p>
    </div>
  </section>
  </body>
</html>

Lượt xem:

# payments/views.py

class SuccessView(TemplateView):
    template_name = 'success.html'


class CancelledView(TemplateView):
    template_name = 'cancelled.html'

URL:

# payments/urls.py

from django.urls import path

from . import views

urlpatterns = [
    path('', views.HomePageView.as_view(), name='home'),
    path('config/', views.stripe_config),
    path('create-checkout-session/', views.create_checkout_session),
    path('success/', views.SuccessView.as_view()), # new
    path('cancelled/', views.CancelledView.as_view()), # new
]

Ok, làm mới trang web tại http: // localhost: 8000 / . Nhấp vào nút thanh toán và sử dụng 4242 4242 4242 4242lại số thẻ tín dụng cùng với phần còn lại của thông tin giả. Gửi khoản thanh toán. Bạn sẽ được chuyển hướng trở lại http: // localhost: 8000 / success / .

Để xác nhận một khoản phí đã thực sự được thực hiện, hãy quay lại trang tổng quan Stripe trong "Thanh toán":

Thanh toán sọc

Để xem xét, chúng tôi đã sử dụng khóa bí mật để tạo ID phiên Checkout duy nhất trên máy chủ. Sau đó, ID này được sử dụng để tạo một phiên bản Checkout, mà người dùng cuối được chuyển hướng đến sau khi nhấp vào nút thanh toán. Sau khi khoản phí xảy ra, họ sẽ được chuyển hướng trở lại trang thành công.

Lưu lượng:

Nhận khóa dành cho nhà xuất bản

  • Gửi yêu cầu XHR từ máy khách đến máy chủ yêu cầu khóa có thể xuất bản
  • Phản hồi bằng phím
  • Sử dụng khóa để tạo một phiên bản mới của Stripe.js

Tạo phiên thanh toán

  • Gửi một yêu cầu XHR khác đến máy chủ yêu cầu ID phiên Checkout mới
  • Tạo Phiên thanh toán mới và gửi lại ID
  • Chuyển hướng đến trang thanh toán để người dùng hoàn tất việc mua hàng của họ

Chuyển hướng người dùng một cách thích hợp

  • Chuyển hướng đến trang thành công sau khi thanh toán thành công
  • Chuyển hướng đến trang hủy sau khi thanh toán bị hủy

Xác nhận thanh toán bằng Stripe Webhooks

  • Thiết lập điểm cuối webhook
  • Kiểm tra điểm cuối bằng Stripe CLI
  • Đăng ký điểm cuối với Stripe

Xác nhận thanh toán bằng Stripe Webhooks

Ứng dụng của chúng tôi hoạt động tốt tại thời điểm này, nhưng chúng tôi vẫn không thể xác nhận thanh toán theo chương trình và có thể chạy một số mã nếu thanh toán thành công. Chúng tôi đã chuyển hướng người dùng đến trang thành công sau khi họ kiểm tra, nhưng chúng tôi không thể chỉ dựa vào trang đó vì xác nhận thanh toán xảy ra không đồng bộ.

There are two types of events in Stripe and programming in general: Synchronous events, which have an immediate effect and results (e.g., creating a customer), and asynchronous events, which don't have an immediate result (e.g., confirming payments). Because payment confirmation is done asynchronously, the user might get redirected to the success page before their payment is confirmed and before we receive their funds.

Một trong những cách dễ nhất để nhận thông báo khi thanh toán được thực hiện là sử dụng gọi lại hoặc cái gọi là Stripe webhook . Chúng tôi sẽ cần tạo một điểm cuối đơn giản trong ứng dụng của mình, Stripe sẽ gọi bất cứ khi nào một sự kiện xảy ra (tức là khi người dùng mua một chiếc áo phông). Bằng cách sử dụng webhook, chúng tôi có thể hoàn toàn chắc chắn rằng khoản thanh toán đã được thực hiện thành công.

Để sử dụng webhook, chúng ta cần:

  1. Thiết lập điểm cuối webhook
  2. Kiểm tra điểm cuối bằng Stripe CLI
  3. Đăng ký điểm cuối với Stripe

Phần này được viết bởi Nik Tomazic .

Điểm cuối

Tạo chế độ xem mới được gọi là chế độ xem stripe_webhooksẽ in thông báo mỗi khi thanh toán được thực hiện thành công:

# payments/views.py

@csrf_exempt
def stripe_webhook(request):
    stripe.api_key = settings.STRIPE_SECRET_KEY
    endpoint_secret = settings.STRIPE_ENDPOINT_SECRET
    payload = request.body
    sig_header = request.META['HTTP_STRIPE_SIGNATURE']
    event = None

    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, endpoint_secret
        )
    except ValueError as e:
        # Invalid payload
        return HttpResponse(status=400)
    except stripe.error.SignatureVerificationError as e:
        # Invalid signature
        return HttpResponse(status=400)

    # Handle the checkout.session.completed event
    if event['type'] == 'checkout.session.completed':
        print("Payment was successful.")
        # TODO: run some custom code here

    return HttpResponse(status=200)

stripe_webhookbây giờ đóng vai trò là điểm cuối webhook của chúng tôi. Ở đây, chúng tôi chỉ tìm kiếm các checkout.session.completedsự kiện được gọi bất cứ khi nào thanh toán thành công, nhưng bạn có thể sử dụng cùng một mẫu cho các sự kiện Stripe khác .

Đảm bảo thêm HttpResponsenhập vào đầu:

from django.http.response import JsonResponse, HttpResponse

Điều duy nhất cần làm để làm cho điểm cuối có thể truy cập được là đăng ký nó trong urls.py :

# payments/urls.py

from django.urls import path

from . import views

urlpatterns = [
    path('', views.HomePageView.as_view(), name='home'),
    path('config/', views.stripe_config),
    path('create-checkout-session/', views.create_checkout_session),
    path('success/', views.SuccessView.as_view()),
    path('cancelled/', views.CancelledView.as_view()),
    path('webhook/', views.stripe_webhook), # new
]

Kiểm tra webhook

Chúng tôi sẽ sử dụng Stripe CLI để kiểm tra webhook.

Sau khi tải xuống và cài đặt , hãy chạy lệnh sau trong cửa sổ đầu cuối mới để đăng nhập vào tài khoản Stripe của bạn:

$ stripe login

Lệnh này sẽ tạo mã ghép nối:

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)

Bằng cách nhấn Enter, CLI sẽ mở trình duyệt web mặc định của bạn và yêu cầu quyền truy cập thông tin tài khoản của bạn. Hãy tiếp tục và cho phép truy cập. Quay lại thiết bị đầu cuối của bạn, bạn sẽ thấy một cái gì đó tương tự như:

> Done! The Stripe CLI is configured for Django 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.

Tiếp theo, chúng ta có thể bắt đầu lắng nghe các sự kiện Stripe và chuyển tiếp chúng đến điểm cuối của chúng ta bằng lệnh sau:

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

Điều này cũng sẽ tạo ra một bí mật ký webhook:

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

Để khởi tạo điểm cuối, hãy thêm bí mật vào tệp settings.py :

# djangostripe/settings.py

STRIPE_ENDPOINT_SECRET = '<your webhook signing secret here>'

Stripe bây giờ sẽ chuyển tiếp các sự kiện đến điểm cuối của chúng tôi. Để kiểm tra, hãy chạy một khoản thanh toán thử nghiệm khác thông qua với 4242 4242 4242 4242. Trong thiết bị đầu cuối của bạn, bạn sẽ thấy Payment was successful.thông báo.

Sau khi hoàn tất, hãy dừng stripe listen --forward-to localhost:8000/webhook/quá trình.

Nếu bạn muốn xác định người dùng thực hiện mua hàng, bạn có thể sử dụng client_reference_id để đính kèm một số loại nhận dạng người dùng vào phiên Stripe.

Ví dụ:

# payments/views.py

@csrf_exempt
def create_checkout_session(request):
    if request.method == 'GET':
        domain_url = 'http://localhost:8000/'
        stripe.api_key = settings.STRIPE_SECRET_KEY
        try:
            checkout_session = stripe.checkout.Session.create(
                # new
                client_reference_id=request.user.id if request.user.is_authenticated else None,
                success_url=domain_url + 'success?session_id={CHECKOUT_SESSION_ID}',
                cancel_url=domain_url + 'cancelled/',
                payment_method_types=['card'],
                mode='payment',
                line_items=[
                    {
                        'name': 'T-shirt',
                        'quantity': 1,
                        'currency': 'usd',
                        'amount': '2000',
                    }
                ]
            )
            return JsonResponse({'sessionId': checkout_session['id']})
        except Exception as e:
            return JsonResponse({'error': str(e)})

Đăng ký điểm cuối

Cuối cùng, sau khi triển khai ứng dụng của mình, bạn có thể đăng ký điểm cuối trong trang tổng quan Stripe, trong Nhà phát triển> Webhooks . Điều này sẽ tạo ra một bí mật ký webhook để sử dụng trong ứng dụng sản xuất của bạn.

Ví dụ:

Django webhook

Lưu lượng:

Nhận khóa dành cho nhà xuất bản

  • Gửi yêu cầu XHR từ máy khách đến máy chủ yêu cầu khóa có thể xuất bản
  • Phản hồi bằng phím
  • Sử dụng khóa để tạo một phiên bản mới của Stripe.js

Tạo phiên thanh toán

  • Gửi một yêu cầu XHR khác đến máy chủ yêu cầu ID phiên Checkout mới
  • Tạo Phiên thanh toán mới và gửi lại ID
  • Chuyển hướng đến trang thanh toán để người dùng hoàn tất việc mua hàng của họ

Chuyển hướng người dùng một cách thích hợp

  • Chuyển hướng đến trang thành công sau khi thanh toán thành công
  • Chuyển hướng đến trang hủy sau khi thanh toán bị hủy

Xác nhận thanh toán bằng Stripe Webhooks

  • Thiết lập điểm cuối webhook
  • Kiểm tra điểm cuối bằng Stripe CLI
  • Đăng ký điểm cuối với Stripe

Cái gì tiếp theo

Trên một trang web trực tiếp, bắt buộc phải có HTTPS để kết nối của bạn được an toàn. Ngoài ra, mặc dù chúng tôi đã mã hóa cứng các khóa API và bí mật ký webhook để đơn giản hóa, nhưng chúng thực sự nên được lưu trữ trong các biến môi trường. Có thể bạn cũng sẽ muốn lưu trữ domain_urldưới dạng một biến môi trường.

Lấy mã từ kho thanh toán django-sọc trên GitHub.

Nguồn:  https://testdriven.io

#django #stripe 

Ananya Gupta

Ananya Gupta

1597123834

Main Pros and Cons of Django As A Web Framework for Python Developers

Django depicts itself as “the web system for fussbudgets with cutoff times”. It was intended to help Python engineers take applications from idea to consummation as fast as could be expected under the circumstances.

It permits fast turn of events on the off chance that you need to make a CRUD application with batteries included. With Django, you won’t need to rehash an already solved problem. It just works and lets you center around your business rationale and making something clients can utilize.

Pros of Django

“Batteries included” theory

The standard behind batteries-included methods normal usefulness for building web applications accompanies the system, not as isolated libraries.

Django incorporates much usefulness you can use to deal with normal web advancement undertakings. Here are some significant level functionalities that Django gives you, which else you need to stay together if you somehow happened to utilize a small scale structure:

ORM

Database relocations

Client validation

Administrator board

Structures

Normalized structure

Django as a system proposes the right structure of an undertaking. That structure helps designers in making sense of how and where to execute any new component.

With a generally acknowledged venture structure that is like numerous tasks, it is a lot simpler to discover online good arrangements or approach the network for help. There are numerous energetic Python designers who will assist you with comprehending any issue you may experience.

Django applications

Django applications (or applications for short) permit designers to separate a task into numerous applications. An application is whatever is introduced by putting in settings.INSTALLED_APPS. This makes it simpler for engineers to add usefulness to the web application by coordinating outer Django applications into the venture.

There are many reusable modules and applications to accelerate your turn of events learn through Online Django Class and Check the Django website.

Secure of course

Django gives great security assurance out of the crate and incorporates avoidance components for basic assaults like SQL Injection (XSS) and Cross-site Request Forgery (CSRF). You can discover more subtleties in the official security diagram control.

REST structure for building APIs

Django REST Framework, commonly condensed “DRF”, is a Python library for building APIs. It has secluded and adaptable engineering that functions admirably for both straightforward and complex web APIs.

DRF gives a lot of verification and authorization strategies out of the case. It is an adaptable, full-included library with measured and adjustable engineering. It accompanies nonexclusive classes for CRUD tasks and an implicit API program for testing API endpoints.

GraphQL structure for building APIs

Huge REST APIs regularly require a lot of solicitations to various endpoints to recover every single required datum. GraphQL it’s a question language that permits us to share related information in a lot simpler design. For a prologue to GraphQL and an outline of its ideas, if it’s not too much trouble allude to the authority GraphQL documentation.

Graphene-Django gives reflections that make it simple to add GraphQL usefulness to your Django venture. Ordinary Django models, structures, validation, consent arrangements, and different functionalities can be reused to manufacture GraphQL blueprint. It additionally gives an implicit API program for testing API endpoints.

Cons of Django

Django ORM

Django ORM, made before SQLAlchemy existed, is currently much sub-par compared to SQLAlchemy. It depends on the Active Record design which is more regrettable than the Unit of Work design embraced by SQLAlchemy. This implies, in Django, models can “spare” themselves and exchanges are off as a matter of course, they are a bit of hindsight. Peruse more in Why I kind of aversion Django.

Django advances course popularity increses day by day:

Django is huge and is viewed as strong bit of programming. This permits the network to create several reusable modules and applications yet has additionally restricted the speed of advancement of the Django. On head of that Django needs to keep up in reverse similarity, so it advances gradually.

Rundown - Should I use Django as a Python designer?

While Django ORM isn’t as adaptable as SQLAlchemy and the enormous environment of reusable modules and applications hinders structure advancement - plainly Django ought to be the best option web system for Python engineers.

Elective, light systems, similar to Flask, while offering a retreat from Django huge biological system and designs, in the long haul can require substantially more additional libraries and usefulness, in the end making many experienced Python engineers winding up wishing they’d began with Django.

Django undertaking’s security and network have become enormously over the previous decade since the system’s creation. Official documentation and instructional exercises are probably the best anyplace in programming advancement. With each delivery, Django keeps on including huge new usefulness.

#django online training #django online course #online django course #django course #django training #django certification course