1659926940
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.
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à:
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:
mode=subscription
)checkout.session.completed
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:
mode=setup
) để thu thập thông tin thanh toáncheckout.session.completed
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.
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:
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.
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à runserver
khở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.
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":
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 .
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ỳ":
Nhấp vào "Lưu sản phẩm".
Tiếp theo, lấy ID API của giá:
Lưu ID trong tệp settings.py như sau:
# djangostripe/settings.py
STRIPE_PRICE_ID = '<enter your stripe price id>'
Để 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_required
trang 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ệ home
quan đ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 home
chế độ xem, họ sẽ được chuyển hướng đến chế độ đã LOGIN_REDIRECT_URL
xá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 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_APPS
trong 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.
Để 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 StripeCustomer
này sẽ lưu trữ Stripe customerId
và subscriptionId
liê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
customerId
vàsubscriptionId
từ 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
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.
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
]
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 fetch
yê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 publicKey
nhằm lấy khóa có thể xuất bả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.
Đầ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_url
và cancel_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)})
Đă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ý:
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.
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.html và hủ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.
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:
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
]
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.
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:
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ý:
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ó
incomplete
khác là ,,,, hoặc .incomplete_expiredtrialingactivepast_duecanceledunpaid
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:
Bạn cũng sẽ muốn sử dụng các biến môi trường cho domain_url
khó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
1659926940
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.
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à:
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:
mode=subscription
)checkout.session.completed
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:
mode=setup
) để thu thập thông tin thanh toáncheckout.session.completed
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.
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:
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.
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à runserver
khở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.
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":
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 .
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ỳ":
Nhấp vào "Lưu sản phẩm".
Tiếp theo, lấy ID API của giá:
Lưu ID trong tệp settings.py như sau:
# djangostripe/settings.py
STRIPE_PRICE_ID = '<enter your stripe price id>'
Để 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_required
trang 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ệ home
quan đ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 home
chế độ xem, họ sẽ được chuyển hướng đến chế độ đã LOGIN_REDIRECT_URL
xá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 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_APPS
trong 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.
Để 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 StripeCustomer
này sẽ lưu trữ Stripe customerId
và subscriptionId
liê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
customerId
vàsubscriptionId
từ 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
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.
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
]
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 fetch
yê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 publicKey
nhằm lấy khóa có thể xuất bả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.
Đầ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_url
và cancel_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)})
Đă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ý:
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.
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.html và hủ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.
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:
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
]
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.
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:
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ý:
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ó
incomplete
khác là ,,,, hoặc .incomplete_expiredtrialingactivepast_duecanceledunpaid
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:
Bạn cũng sẽ muốn sử dụng các biến môi trường cho domain_url
khó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
1620177818
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.
#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
1620185280
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:
Describe 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
1660393740
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 .
Hiện có ba cách để chấp nhận thanh toán một lần với Stripe:
Bạn nên sử dụng cái nào?
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.
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_APPS
cấ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à runserver
khở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:
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":
Then in the left sidebar click on "API keys":
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 :
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":
Thêm tên sản phẩm, nhập giá và chọn "Một lần":
Nhấp vào "Lưu sản phẩm".
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
Create Checkout Session
Redirect the User Appropriately
Confirm Payment with Stripe Webhooks
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:
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
]
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 fetch
yê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 publicKey
nhằ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
Tạo phiên thanh toán
Chuyển hướng người dùng một cách thích hợp
Xác nhận thanh toán bằng Stripe Webhooks
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.
Đầ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_url
và cancel_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
]
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:
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
Tạo phiên thanh toán
Chuyển hướng người dùng một cách thích hợp
Xác nhận thanh toán bằng Stripe Webhooks
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 4242
lạ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":
Để 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
Tạo phiên thanh toán
Chuyển hướng người dùng một cách thích hợp
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:
Phần này được viết bởi Nik Tomazic .
Tạo chế độ xem mới được gọi là chế độ xem stripe_webhook
sẽ 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_webhook
bâ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.completed
sự 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 HttpResponse
nhậ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
]
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)})
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ụ:
Lưu lượng:
Nhận khóa dành cho nhà xuất bản
Tạo phiên thanh toán
Chuyển hướng người dùng một cách thích hợp
Xác nhận thanh toán bằng Stripe Webhooks
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_url
dướ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
1597123834
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