こんにちは、DXCEL WAVEの運営者(@dxcelwave )です!
Python Django フレームワーク上にStripe APIを活用したオンライン決済機能 を導入するDjanogoでオンライン決済画面 を実装する 顧客が商品を購入したタイミングで通知を受け取る Django DBで商品購入履歴を管理 する
目次
【Django】Stripeを用いたオンライン決済機能の導入概要
VIDEO
本記事では、上記動画に示すようなオンライン決済画面をDjangoフレームワークで実装する方法 について解説します。オンライン決済機能導入に際して、Stripe が提供する決済APIを活用します。
Stripeとは
【Stripe公式】https://stripe.com/jp
stripe(ストライプ) とは、アメリカ(サンフランシスコ)とアイルランド(ダブリン)に本社を置く、オンライン決済サービスを提供するSaaS企業 です。
eコマースWebサイトやモバイルアプリ向けに、多様な決済処理アプリケーションを提供しています。また、開発者向けにAPIも提供しています。
stripeの決済サービスを活用することで具体的に次のようなことができるようになります。
eコマースWebサイトやモバイルアプリ向けオンライン決済画面の簡易作成 サブスクリプション機能の構築 決済リンクの簡易作成 オンライン請求書の発行・管理
stripe API活用によるDjango決済機能実装アーキテクチャ
本記事ではStripeドキュメント(構築済みチェックアウト画面) をDjangoで実装する方法について解説します。
その際、全体のアーキテクチャ像は上記になる想定です。
Django Viewでは、Stripe APIを呼び出す仕組み構築 Django ViewからPUTリクエストを受け取ったStripeは、オンライン決済サービスを提供 顧客は、Django Viewを経由してStripeオンライン決済(商品購入)が可能に Stripeオンライン決済後、顧客の購買履歴をDjango DBが受け取りDBにて管理
【事前準備】Stripeオンライン決済機能導入前のDjango環境設定
Stripeのオンライン決済機能を実装するために、Django環境を構築していきます。
以下のタスク事項を事前に対応しておきましょう。以下1つずつ対応方法を解説します。
事前要対応タスク一覧
Pythonライブラリのインストール Djangoプロジェクト・アプリケーションの構築 stripeアカウントを作成 stripeパブリックキーとシークレットキーを取得
Pythonライブラリのインストール
本記事では以下に示すバージョンのPythonライブラリを利用していきます。
asgiref==3.4.1 Django==4.0.3 pytz==2021.1 sqlperse==0.4.2 stripe==3.5.0
Djangoのバージョンは4.0.0以降を活用するようにしましょう。
DjangoからstripeのAPIを呼び出すには、stripeライブラリの利用が必須です。インタラクティブシェル(Macはターミナル、Windowsはコマンドプロンプト)を開き、以下を実行しましょう。
Djangoプロジェクト・アプリケーションの構築
本記事ではDjangoプロジェクトフォルダ(Project_Folder)とアプリケーションフォルダ(App_Folder)を用意した上記フォルダ構成を初期設定として利用します。
Djangoプロジェクトおよびアプリケーション は、以下記事を参考にしながら構築しましょう。
【Django】開発環境構築・プロジェクト開始チュートリアル|Python・Webアプリ作成入門#1
この記事は「Djangoフレームワークを活用したウェブアプリをゼロから開発したい方向け」です。事前に必要な環境設定方法や新規プロジェクト作成、簡易なWebアプリ作成に至るまでの流れを全て網羅した内容となっております。
stripeアカウントを作成・APIキーの取得
DjangoでStripeのオンライン決済機能を実装する場合、事前にstripeアカウントを作成し、APIキーを取得しておく 必要があります。以下の記事を参考にし、APIキーを取得しましょう。
後続で活用するAPIキー
テスト環境用のAPIキー(パブリックキー) テスト環境用のシークレットキー(テストキー)
APIキーの取得手順
【Stripe】APIキー・シークレット取得方法|オンライン決済システムの導入支援
EコマースWebサイトまたはモバイルアプリケーションにオンライン決済システムを導入したい開発者向けに、Stripeの決済機能を活用できるようになる「APIキーの取得方法」を解説します。
【実践】stripeオンライン決済機能をDjangoで実装する方法
ここから実際にDjangoフレームワークからStripe APIを呼び出し、オンライン決済機能を活用するに至るまでの方法を解説していきます。
以下手順に沿ってPythonコーディング概要と方法をそれぞれ詳しく解説していきます。
settings.pyの設定 データベース設定 画面表示設定(views.py) 画面表示設定(urls.py) フロント画面作成(HTML) フロント画面作成(CSS) 動作確認
settings.pyの設定
はじめに、Project_Folder > settings.py
にコードを修正していきます。
以下の手順に沿って作業を進めましょう。
① ファイルパスの設定
HTML・CSS・画像ファイルを格納するためのディレクトリ設定を行います。settings.py上部のPathを以下のように編集しましょう。
Before
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
After
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Tepmpateフォルダへの絶対パスを定義
TEMPLATE_DIR = BASE_DIR / "Template"
# staticフォルダへの絶対パスを定義
STATIC_DIR = BASE_DIR / "static"
# メディアフォルダへの絶対パスを定義
MEDIA_DIR = BASE_DIR / "media"
② テンプレートフォルダのディレクトリ設定
settings.pyにテンプレート(HTMLファイルを格納するフォルダ)のディレクトリを設定します。以下のようにコードを編集しましょう。
Before
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
After
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [TEMPLATE_DIR], # 追加
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
※テンプレートフォルダは④で後述で作成します。
③ staticとmediaフォルダのディレクトリ設定
settings.pyにstatic(CSSファイルを格納するフォルダ)とmedia(商品写真を格納するフォルダ)のディレクトリを設定します。以下のようにコードを編集しましょう。
Before
After
# CSSファイル格納用のフォルダ
STATIC_URL = 'static/'
STATICFILES_DIRS = [STATIC_DIR,]
# 商品写真アップロード用のフォルダ
MEDIA_ROOT = MEDIA_DIR
MEDIA_URL = "/media/"
※staticおよびmediaフォルダは④で後述で作成します。
④ Template・static・mediaフォルダ作成
(1) 上図ディレクトリ構成となるようTemplate
・static
・media
フォルダを作成します。
Project_FolderおよびApp_Folderと同じディレクトリに作成します。
(2) 前述作成の3つのフォルダの中にそれぞれ該当のファイルを作成しましょう。
フォルダ名 追加作成物 形式 Template ・product-top.html ・success.html ・cancel.html ファイル static ・style.css ファイル media ・product_files フォルダ
※上記で作成したhtmlおよびcssファイルの中身は、後述の「フロント画面作成」で言及します。
⑤ 決済後のメール送信設定
オンライン決済時にメールが自動送信できるような設定を追加します。AUTH_PASSWORD_VALIDATORS
直下に以下を追記しましょう。
Before
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
After
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Email設定
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
⑥ StripeのAPIキーを設定
Stripe APIキー(STRIPE_PUBLIC_KEY
)とシークレットキー(STRIPE_WEBHOOK_SECRET
)を追記します。以下のコードをStripe画面で取得した具体的な値に書き換えた上で、settings.pyに追記しましょう。
# Stripeのパブリックキー
STRIPE_PUBLIC_KEY = 'ここにSTRIPE_PUBLIC_KEYを入力'
# Stripeのシークレットキー
STRIPE_SECRET_KEY = 'ここにSTRIPE_SECRET_KEYを入力'
# StripeのWebhookのシークレットキー
STRIPE_WEBHOOK_SECRET = 'ここにSTRIPE_WEBHOOK_SECRETを入力(現時点でここはまだ修正しなくてOKです)'
※STRIPE_WEBHOOK_SECRET
は、後述のStripe Webhookの見出しで取得方法を解説しています。現時点でこのキーはまだ活用しないため、上記をそのままコピー&ペーストしてもらうだけで問題なしです。
データベース設定
続いてデータベース設定を行うために、App_Folder > models.py, admin.py
にコードを記載していきます。
① models.pyに商品マスタと価格マスタを作成
オンライン決済画面に表示する商品情報を登録できるようにするためのテーブルを作成します。商品マスタ と価格マスタ の2つを用意します。models.pyを開き、以下のコードに書き換えましょう。
コード
from django.db import models
# 商品マスタ
class Product(models.Model):
# 商品名
name = models.CharField(max_length=100)
# 商品概要
description = models.CharField(max_length=255, blank=True, null=True)
# 商品ID(★stripeの商品IDを値に用いる)
stripe_product_id = models.CharField(max_length=100)
# 商品写真登録用のファイル
file = models.FileField(upload_to="product_files/", blank=True, null=True)
# 商品詳細ページのリンク
url = models.URLField()
# admin画面で商品名表示
def __str__(self):
return self.name
# 価格マスタ
class Price(models.Model):
# 外部キーで商品マスタを紐付け
product = models.ForeignKey(Product, related_name='Prices', on_delete=models.CASCADE)
# 価格ID(★stripeの価格IDを値に用いる)
stripe_price_id = models.CharField(max_length=100)
# 価格
price = models.IntegerField(default=0)
# Django画面に表示する価格
def get_display_price(self):
return self.price
テーブル概要(商品マスタ)
商品マスタの項目概要は次のようになります。
項目名 概要 name 商品名 description 商品の概要 stripe_product_id Stripeに登録する商品をDjango DBの商品レコードと紐づけるための商品ID file 商品の写真 url 商品詳細ページのリンク
テーブル概要(価格マスタ)
価格マスタの項目概要は次のようになります。
項目名 概要 stripe_price_id Stripeに登録する商品をDjango DBの商品レコードと紐づけるための価格ID price 商品の価格
② マイグレーションを行い、データベースにテーブル情報を適用する
今回データベースは、Djangoに標準で搭載されているSQLite3 を活用します。以下の手順に従い、SQLiteに商品マスタと価格マスタのテーブル情報を反映させましょう。
(1) インタラクティブシェル(Macはターミナル、Windowsはコマンドプロンプト)を開き、manage.py
のあるディレクトリまで移動しましょう。そして、以下を入力しマイグレーションを実行します。
python3 manage.py makemigrations
(2) 上記を実行した際、以下の結果が得られれば成功です。
Migrations for 'App_Folder':
App_Folder/migrations/0001_initial.py
- Create model Product
- Create model Price
(3) 続いて、以下のコードを入力・実行します。
python3 manage.py migrate
(4) 上記を実行した際、以下の結果が得られればマイグレーション完了です。
Applying App_Folder.0001_initial... OK
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying auth.0012_alter_user_first_name_max_length... OK
Applying sessions.0001_initial... OK
【参考】モデルのマイグレーションはこちらの記事で詳しく解説しています。
【Django】モデルの操作・データベース作成・削除|PythonによるWebアプリ開発(models.py)#6
Djangoのモデルの構築方法を詳しく紹介します。モデルとは何か?という観点から、モデル構築手順に至るまで知ることができます。また、モデルからデータベースにテーブル登録した後の不具合対応として、データベースの削除方法にも言及します。
③ スーパーユーザーを登録する
データベースに商品レコードを登録するために、管理画面にアクセスします。管理画面を開くには、スーパーユーザー を作成が必要です。以下の手順に従い、スーパーユーザー作成しましょう。
(1) manage.pyがあるディレクトリに移動し、インタラクティブシェルで以下を実行します。
python3 manage.py createsuperuser
(2) 上記実行後、ユーザー名、メールアドレス、パスワードの順で入力します。
Username: username
Email address: test@gmail.com
Password:
Password (again):
(3) 上記の入力完了後、以下が出力されればスーパーユーザーの設定完了です。
Superuser created successfully.
(4) ここで設定したUsername
とPassword
はお手元にメモとして残しておきましょう。
④ admin.pyを設定しマスタ情報を確認する
Djangoの管理画面から前述で作成した商品マスタおよび価格マスタが閲覧できるように、admin.pyを修正します。App_Folder > admin.py
を開き、以下のコードに書き換えましょう。
Before
from django.contrib import admin
# Register your models here.
After
from django.contrib import admin
from .models import Product, Price # models.pyで作成したマスタを読込
class PriceInlineAdmin(admin.TabularInline):
model = Price
extra = 0
class ProductAdmin(admin.ModelAdmin):
inlines = [PriceInlineAdmin]
# 管理画面に商品マスタと価格マスタを表示
admin.site.register(Product, ProductAdmin)
admin.site.register(Price)
⑥ オンライン決済用のテスト商品を作成する(Stripe画面)
オンライン決済機能実装に際して、テスト用の商品を作成します。Stripeダッシュボード画面 を開き、以下の手順に沿って商品を作成しましょう。
(1) Stripeダッシュボード画面 に遷移し「商品タブ」を選択しましょう。
(2) これはオンラインで販売したい商品が登録できる画面です。
今回は動作確認を目的とするため「テスト環境 」を選択し、商品を追加するようにしましょう。
(3) 商品情報を登録していきます。上図の記入欄を入力しましょう。
(4) 料金体系を登録します。数量・ユニット指定による価格管理を行いたい場合、それに対応した料金体系モデルを選択すると良いですが、今回は料金体系モデルを「標準の料金体系 」を指定して以下解説することとします。
(5) 購入方法にはサブスクリプションに対応した「継続」と一括購入に対応した「一括」が用意されています。本記事の趣旨は後者であるため、「一括 」を選択するようにしましょう。
(6) 最後に、右上の「商品を保存」ボタンをクリックします。
(7) 「商品タブ」を再びクリックし、商品が登録されたことを確認しましょう。
⑦ Stirpeダッシュボード画面から「商品ID」と「価格ID」を取得する
Stripeダッシュボード画面で登録した商品情報をDjango DB上にも登録します。ここでのポイントは、Django DBに登録する商品情報がStripe上の商品情報と正確に紐づけられることです。 これらを紐づけるキーとしてStripeダッシュボード画面から取得可能なstripe_product_id
(商品ID)とstripe_price_id
(価格ID)を用います。以下の手順に従い、各種IDを取得しましょう。
(1) 前述で作成した商品をクリックし、商品詳細画面に遷移します。
(2) 商品詳細画面を開くとstripe_product_id
とstripe_price_id
が上図の位置で確認できます。
それぞれのIDは後述のDjango DB登録で利用するため、お手元にメモとして残しておきましょう。
⑧ Stirpeに登録した商品情報をDjango DBに登録
Django DB(SQLite3)にStripeダッシュボード画面で作成した商品・価格情報を登録します。
(1) ブラウザからDjango管理画面を開きましょう。
(2) 続いて、「Products 」を選択しましょう。
(3) 上記の画面が表示されたら「Add Product 」を選択します。
(4) Stripe画面に登録した商品・価格情報をDjango DBにレコードとして登録します。
(5) 上図入力欄を全て記入しましょう。価格情報(Price)はAdd Priceをクリックすると追加できます。
(6) 入力完了後「保存ボタン」をクリックしましょう。
これでDjango DBへの商品登録が完了です!
画面表示設定|views.py
続いてWebアプリ画面の表示設定を行うために、App_Folder > views.py
を修正していきます。
以下のコードに書き換えましょう。
views.pyコード全量
import json
import stripe
from django.conf import settings
from django.http import JsonResponse, HttpResponse
from django.shortcuts import redirect
from django.core.mail import send_mail
from django.views.generic import TemplateView
from django.views.generic import ListView
from django.views.decorators.csrf import csrf_exempt
from django.views import View
from .models import Product
from .models import Price
# STRIPEのシークレットキー
stripe.api_key = settings.STRIPE_SECRET_KEY
# WEBHOOKのシークレットキー
endpoint_secret = settings.STRIPE_WEBHOOK_SECRET
# 決済成功画面
class SuccessPageView(TemplateView):
template_name = 'success.html'
# 決済キャンセル画面
class CancelPageView(TemplateView):
template_name = 'cancel.html'
class ProductTopPageView(ListView):
# 商品マスタ
model = Product
# ページリンク
template_name = "product-top.html"
#レコード情報をテンプレートに渡すオブジェクト
context_object_name = "product_list"
# 決済画面
class CreateCheckoutSessionView(View):
def post(self, request, *args, **kwargs):
# 商品マスタ呼出
product = Product.objects.get(id=self.kwargs["pk"])
price = Price.objects.get(product=product)
# ドメイン
YOUR_DOMAIN = "http://127.0.0.1:8000"
# 決済用セッション
checkout_session = stripe.checkout.Session.create(
# 決済方法
payment_method_types=['card'],
# 決済詳細
line_items=[
{
'price': price.stripe_price_id, # 価格IDを指定
'quantity': 1, # 数量
},
],
# POSTリクエスト時にメタデータ取得
metadata = {
"product_id":product.id,
},
mode='payment', # 決済手段(一括)
success_url=YOUR_DOMAIN + '/success/', # 決済成功時のリダイレクト先
cancel_url=YOUR_DOMAIN + '/cancel/', # 決済キャンセル時のリダイレクト先
)
return redirect(checkout_session.url)
【参考】views.pyコード個別解説
上記のコードについて1つずつ解説していきます。
APIキーの呼出設定
views.pyには、決済画面を利用するためにStripeにpostリクエストを送る処理を記述しています。
Stripeにキー情報を送信するために、settings.pyのAPIキー情報を読み込んでいます。
from django.conf import settings
# STRIPEのシークレットキー
stripe.api_key = settings.STRIPE_SECRET_KEY
# WEBHOOKのシークレットキー
endpoint_secret = settings.STRIPE_WEBHOOK_SECRET
フロント画面の表示設定
商品トップ画面、決済成功画面、決済キャンセル画面 という3つの画面を取り扱います。それぞれの画面を表示するためのビュークラスを以下のコードで表現しています。
# 決済成功画面
class SuccessPageView(TemplateView):
template_name = 'success.html'
# 決済キャンセル画面
class CancelPageView(TemplateView):
template_name = 'cancel.html'
class ProductTopPageView(ListView):
# 商品マスタ
model = Product
# ページリンク
template_name = "product-top.html"
#レコード情報をテンプレートに渡すオブジェクト
context_object_name = "product_list"
商品のチェックアウトセッションの作成
データベースに登録された商品別にStripeの決済APIをコールするためのクラスを以下のように定義しています。
# 決済画面
class CreateCheckoutSessionView(View):
def post(self, request, *args, **kwargs):
# 商品マスタ呼出
product = Product.objects.get(id=self.kwargs["pk"])
price = Price.objects.get(product=product)
# ドメイン
# YOUR_DOMAIN = "http://127.0.0.1:8000"
# # 決済用セッション
# checkout_session = stripe.checkout.Session.create(
# # 決済方法
# payment_method_types=['card'],
# # 決済詳細
# line_items=[
# {
# 'price': price.stripe_price_id, # 価格IDを指定
# 'quantity': 1, # 数量
# },
# ],
# # POSTリクエスト時にメタデータ取得
# metadata = {
# "product_id":product.id,
# },
# mode='payment', # 決済手段(一括)
# success_url=YOUR_DOMAIN + '/success/', # 決済成功時のリダイレクト先
# cancel_url=YOUR_DOMAIN + '/cancel/', # 決済キャンセル時のリダイレクト先
# )
# return redirect(checkout_session.url)
Stripe APIをコールするには、商品IDと価格IDを渡す必要があります。
該当のIDをStripeに渡せるように、上記コードでDjango DBから商品・価格レコードを取得しています。
# 決済画面
class CreateCheckoutSessionView(View):
def post(self, request, *args, **kwargs):
# 商品マスタ呼出
# product = Product.objects.get(id=self.kwargs["pk"])
# price = Price.objects.get(product=product)
ドメイン
YOUR_DOMAIN = "http://127.0.0.1:8000"
# 決済用セッション
# checkout_session = stripe.checkout.Session.create(
# # 決済方法
# payment_method_types=['card'],
# # 決済詳細
# line_items=[
# {
# 'price': price.stripe_price_id, # 価格IDを指定
# 'quantity': 1, # 数量
# },
# ],
# # POSTリクエスト時にメタデータ取得
# metadata = {
# "product_id":product.id,
# },
# mode='payment', # 決済手段(一括)
# success_url=YOUR_DOMAIN + '/success/', # 決済成功時のリダイレクト先
# cancel_url=YOUR_DOMAIN + '/cancel/', # 決済キャンセル時のリダイレクト先
# )
# return redirect(checkout_session.url)
Stripe APIを呼び出し、決済完了後にリダイレクトされるドメインを上記のように記述しています。
今回のテスト段階ではローカルサーバーのURL をドメインとして設定するようにしましょう。
上記の例ではlocal:8000
ポートを指定しています。
# 決済画面
class CreateCheckoutSessionView(View):
def post(self, request, *args, **kwargs):
# 商品マスタ呼出
# product = Product.objects.get(id=self.kwargs["pk"])
# price = Price.objects.get(product=product)
ドメイン
# YOUR_DOMAIN = "http://127.0.0.1:8000"
# 決済用セッション
checkout_session = stripe.checkout.Session.create(
# 決済方法
payment_method_types=['card'],
# 決済詳細
line_items=[
{
'price': price.stripe_price_id, # 価格IDを指定
'quantity': 1, # 数量
},
],
# POSTリクエスト時にメタデータ取得
metadata = {
"product_id":product.id,
},
mode='payment', # 決済手段(一括)
success_url=YOUR_DOMAIN + '/success/', # 決済成功時のリダイレクト先
cancel_url=YOUR_DOMAIN + '/cancel/', # 決済キャンセル時のリダイレクト先
)
return redirect(checkout_session.url)
checkout_session
では、決済方法、ラインアイテム、注文金額および量など、Stripeがオンラインで提供する決済画面で顧客に表示される内容を制御しています。
上記コードはセッション作成時にstripe_product_id
およびstripe_price_id
を渡すことで、対象商品の決済画面を呼び出せる仕組みとしています。
最後に決済成功時のリダイレクト先(success_url)とキャンセル時のリダイレクト先(cancel_url)をそれぞれ定義しています。
画面表示設定|urls.py
続いてWebアプリ画面の表示設定を行うために、Project_Folder > urls.py
を修正していきます。
以下のコードに書き換えましょう。
from django.contrib import admin
from django.urls import path
from django.conf import settings # settings.pyの変数
from django.conf.urls.static import static # メディア表示
# App_Folderからviews.pyで定義した関数呼出
from App_Folder.views import (
CreateCheckoutSessionView,
ProductTopPageView,
SuccessPageView,
CancelPageView,
)
urlpatterns = [
path('admin/', admin.site.urls), # 管理画面
path("", ProductTopPageView.as_view(), name="product-top-page"), # 商品トップ
path("create-checkout-session/<pk>/", CreateCheckoutSessionView.as_view(), name="create-checkout-session"), # 個別商品決済画面
path("success/", SuccessPageView.as_view(), name="success"), # 決済成功時にリダイレクト先
path("cancel/", CancelPageView.as_view(), name="cancel"), # 決済キャンセル時のリダイレクト先
]
# メディア表示
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
フロント画面作成|HTML
フロント画面のHTMLファイルにコードを記述します。Template直下の3つのファイルが対象です。
htmlファイル名 概要 product-top.html 商品のトップ画面 success.html Stripeで決済が完了した際にリダイレクトされる画面 cencel.html Stripeで決済キャンセルした際にリダイレクトされる画面
各種HTMLファイルに記述するコード
① product-top.html
<!DOCTYPE html>
{% load static %}
<html>
<head>
<title>商品トップ</title>
<link rel="stylesheet" href='{% static "style.css" %}'/>
<script src="https://polyfill.io/v3/polyfill.min.js?version=3.52.1&features=fetch"></script>
<script src="https://js.stripe.com/v3/"></script>
</head>
<body>
<section>
<h1>商品一覧</h1>
<hr />
<div class="product">
<div class="description">
<!-- 商品一覧をループで表示 -->
{% for product in product_list %}
<h2>{{ product.name }}</h2>
<picture>
<img src="{{product.file.url}}" alt="image" style="width:auto;">
</picture>
<div>{{ product.description }}</div>
<!-- 商品に紐づく価格情報を表示 -->
{% for price_data in product.Prices.all %}
<h4>価格:¥{{ price_data.get_display_price }}</h4>
<form action="{% url 'create-checkout-session' product.id %}" method="POST">
{% csrf_token %}
<button type="submit">購入画面に進む</button>
</form>
{% endfor %}
{% endfor %}
</div>
</div>
</section>
</body>
</html>
② success.html
<html>
<head>
<title>購入完了</title>
</head>
<body>
<section>
<p>この度は商品を購入いただきありがとうございました。</p>
<a href="{% url 'product-top-page' %}">トップページに戻る</a>
</section>
</body>
</html>
③ cancel.html
<html>
<head>
<title>購入キャンセル</title>
</head>
<body>
<section>
<p>他の商品を確認しますか?<a href="{% url 'product-top-page' %}">商品トップページに戻る</a></p>
</section>
</body>
</html>
【参考】テンプレートの概要はこちらの記事で詳しく解説しています。
【Django】テンプレートの作成・HTML/CSS表示|PythonによるWebアプリ開発(Template)#4
Djangoフレームワークを活用するテンプレートの概要および基礎的な活用方法を詳しく知りたい方向けです。まず、Djangoフレームワークにおけるテンプレートの役割をご紹介します。続いて実際の構築・コーディング説明をもとに、HTML/CSSコードを画面に表示させる手順を実演します。
フロント画面作成|CSS
static > style.css
ファイルにCSSコードを記述します。
style.cssのCSSコード全量
/* Variables */
* {
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
font-size: 16px;
-webkit-font-smoothing: antialiased;
display: flex;
justify-content: center;
align-content: center;
height: 100vh;
width: 100vw;
}
h2{
background-color: lightgreen;
padding-top:20px;
padding-bottom:20px;
padding-left:10px;
margin-top:50px;
}
form {
width: 30vw;
min-width: 640px;
align-self: center;
box-shadow: 0px 0px 0px 0.5px rgba(50, 50, 93, 0.1),
0px 2px 5px 0px rgba(50, 50, 93, 0.1), 0px 1px 1.5px 0px rgba(0, 0, 0, 0.07);
border-radius: 7px;
padding: 40px;
}
.hidden {
display: none;
}
#payment-message {
color: rgb(105, 115, 134);
font-size: 16px;
line-height: 20px;
padding-top: 12px;
text-align: center;
}
#payment-element {
margin-bottom: 24px;
}
/* Buttons and links */
button {
background: #5469d4;
font-family: Arial, sans-serif;
color: #ffffff;
border-radius: 4px;
border: 0;
padding: 12px 16px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
display: block;
transition: all 0.2s ease;
box-shadow: 0px 4px 5.5px 0px rgba(0, 0, 0, 0.07);
width: 100%;
}
button:hover {
filter: contrast(115%);
}
button:disabled {
opacity: 0.5;
cursor: default;
}
/* spinner/processing state, errors */
.spinner,
.spinner:before,
.spinner:after {
border-radius: 50%;
}
.spinner {
color: #ffffff;
font-size: 22px;
text-indent: -99999px;
margin: 0px auto;
position: relative;
width: 20px;
height: 20px;
box-shadow: inset 0 0 0 2px;
-webkit-transform: translateZ(0);
-ms-transform: translateZ(0);
transform: translateZ(0);
}
.spinner:before,
.spinner:after {
position: absolute;
content: "";
}
.spinner:before {
width: 10.4px;
height: 20.4px;
background: #5469d4;
border-radius: 20.4px 0 0 20.4px;
top: -0.2px;
left: -0.2px;
-webkit-transform-origin: 10.4px 10.2px;
transform-origin: 10.4px 10.2px;
-webkit-animation: loading 2s infinite ease 1.5s;
animation: loading 2s infinite ease 1.5s;
}
.spinner:after {
width: 10.4px;
height: 10.2px;
background: #5469d4;
border-radius: 0 10.2px 10.2px 0;
top: -0.1px;
left: 10.2px;
-webkit-transform-origin: 0px 10.2px;
transform-origin: 0px 10.2px;
-webkit-animation: loading 2s infinite ease;
animation: loading 2s infinite ease;
}
@-webkit-keyframes loading {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes loading {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@media only screen and (max-width: 600px) {
form {
width: 80vw;
min-width: initial;
}
}
上記、デザインコード例を参考にしましょう。コードはそのまま活用いただくこともできます。
動作確認
python3 manage.py runserver
ここまでできましたら一度動作確認を行いましょう!
ローカルサーバーを立ち上げ決済画面を開きテスト商品を購入してみましょう。
この時、カード番号は4242 4242 4242 4242
と入力します。
Stripe Webhookをもとに顧客支払通知を受け取りDBに登録 する
ここまでで「Stripe APIを活用した決済機能の導入」と「商品決済画面の作成」が完了しました。ここからは「顧客が商品を購入したタイミングで通知を受け取り、支払い履歴をDjango DB上に保存して管理する方法 」について解説します。
StripeではWebhook という方法が利用されています。Webhookとは、オンライン決済が完了したタイミングで、StripeがそのイベントをHTTPのPOSTメソッドで送ってくれるものです。
このWebhookで顧客の商品購入イベントを検知したり、StripeからのレスポンスをDBに保存することで顧客の支払い履歴が管理できるようになります。
【事前準備】Stripe CLIをインストールする
Webhookの利用には、Stripe ILCのインストールが必要です。
① Stripe CLIをインストール
homebrew, Linux, Windowsなど、上記のようなインストール方法が選択できます。
Stripe CLIインストールガイド を開き、お好みの方法でStripe CLIをインストールしましょう。
② Stripe CLIにログインする
前述でStripe CLIのインストールが完了したら、Stripe CLIにログインします。
(1) インタラクティブシェル(Macはターミナル、Windowsはコマンドプロンプト)を開き、以下を入力してCLIにログインしてみましょう。
この時、Djangoのローカルサーバーを起動しているシェルとは別の新たなシェルを立ち上げた上で、CLIにログインするようにします。
(2) 上記実行後、以下ののようなメッセージが表示されます。続けて「Enter 」を押下しましょう。
Your pairing code is: enjoy-enough-outwit-win
This pairing code verifies your authentication with Stripe.
Press Enter to open the browser or visit https://dashboard.stripe.com/stripecli/confirm_auth?t=THQdJfL3x12udFkNorJL8OF1iFlN8Az1 (^C to quit)
(3) ブラウザが立ち上がり、下図のような画面が表示されます。
(4) 「アクセスを許可 」を押下しましょう。
(5) インタラクティブシェルに戻り、以下のコードを実行しましょう。
(6) 実行後「All services are online 」がシェル上に出力されればCLIログイン完了です。
ここで起動中のインタラクティブシェルは後述で再度利用するため閉じないようにしましょう。
イベントハンドラ・DB管理関数を作成|views.py
このセクションでは、顧客の購入イベント(checkout.session.completed
)をStripeから受信するイベントハンドラ関数 をviews.pyに追加します。さらに、Stripeから受信したレスポンスをDBに保存する関数 も併せて作成します。
イベントハンドラ関数とDB管理関数を追記したviews.pyを以下に示します。
stripe_webhook
およびSaveTransaction
が主な更新点になっています。
【全量コード】views.py
import json
import stripe
from django.conf import settings
from django.http import JsonResponse, HttpResponse
from django.shortcuts import redirect
from django.core.mail import send_mail
from django.views.generic import TemplateView
from django.views.generic import ListView
from django.views.decorators.csrf import csrf_exempt
from django.views import View
from .models import Product
from .models import Price
from .models import Transaction # 追加
import datetime # 追加
# STRIPEのシークレットキー
stripe.api_key = settings.STRIPE_SECRET_KEY
# WEBHOOKのシークレットキー
endpoint_secret = settings.STRIPE_WEBHOOK_SECRET
# 決済成功画面
class SuccessPageView(TemplateView):
template_name = 'success.html'
# 決済キャンセル画面
class CancelPageView(TemplateView):
template_name = 'cancel.html'
class ProductTopPageView(ListView):
# 商品マスタ
model = Product
# ページリンク
template_name = "product-top.html"
#レコード情報をテンプレートに渡すオブジェクト
context_object_name = "product_list"
# 決済画面
class CreateCheckoutSessionView(View):
def post(self, request, *args, **kwargs):
# 商品マスタ呼出
product = Product.objects.get(id=self.kwargs["pk"])
price = Price.objects.get(product=product)
# ドメイン
YOUR_DOMAIN = "http://127.0.0.1:8000"
# 決済用セッション
checkout_session = stripe.checkout.Session.create(
# 決済方法
payment_method_types=['card'],
# 決済詳細
line_items=[
{
'price': price.stripe_price_id, # 価格IDを指定
'quantity': 1, # 数量
},
],
# POSTリクエスト時にメタデータ取得
metadata = {
"product_id":product.id,
},
mode='payment', # 決済手段(一括)
success_url=YOUR_DOMAIN + '/success/', # 決済成功時のリダイレクト先
cancel_url=YOUR_DOMAIN + '/cancel/', # 決済キャンセル時のリダイレクト先
)
return redirect(checkout_session.url)
# イベントハンドラ
@csrf_exempt
def stripe_webhook(request):
# サーバーのイベントログからの出力ステートメント
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:
# 有効でないpayload
return HttpResponse(status=400)
except stripe.error.SignatureVerificationError as e:
# 有効でない署名
return HttpResponse(status=400)
# checkout.session.completedイベント検知
if event['type'] == 'checkout.session.completed':
session = event['data']['object']
# イベント情報取得
customer_name = session["customer_details"]["name"] # 顧客名
customer_email = session["customer_details"]["email"] # 顧客メール
product_id = session["metadata"]["product_id"] # 購入商品ID
product = Product.objects.get(id=product_id) # 購入商品情報
product_name = product.name # 購入した商品名
amount = session["amount_total"] # 購入金額(手数料抜き)
# DBに結果を保存
SaveTransaction(product_name, customer_name, customer_email, amount)
# 決済完了後メール送信(Djangoのメール機能利用)
send_mail(
subject = '商品購入完了!', # 件名
message = '{}様\n商品購入ありがとうございます。購入された商品URLはこちら{}'.format(customer_name,product.url), # メール本文
recipient_list = [customer_email], # TO
from_email = 'test@test.com' # FROM
)
# 結果確認
print(session)
return HttpResponse(status=200)
# 顧客の商品購入履歴を保存
def SaveTransaction(product_name, customer_name, customer_email, amount):
# DB保存
saveData = Transaction.objects.get_or_create(
product_name = product_name,
date = datetime.datetime.now(),
customer_name = customer_name,
email = customer_email,
product_amount = amount
)
return saveData
【コード個別解説】views.py
stripe_webhook関数について
Stripeでの顧客の購入イベントはstripe_webhook
関数で検知しています。
顧客のインベントログは、以下に示すstripe.Webhook.construct_event
にて取得しています。
# イベントハンドラ
@csrf_exempt
def stripe_webhook(request):
# サーバーのイベントログからの出力ステートメント
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:
# 有効でないpayload
return HttpResponse(status=400)
except stripe.error.SignatureVerificationError as e:
# 有効でない署名
return HttpResponse(status=400)
# # checkout.session.completedイベント検知
# if event['type'] == 'checkout.session.completed':
# session = event['data']['object']
#
# # イベント情報取得
# customer_name = session["customer_details"]["name"] # 顧客名
# customer_email = session["customer_details"]["email"] # 顧客メール
# product_id = session["metadata"]["product_id"] # 購入商品ID
# product = Product.objects.get(id=product_id) # 購入商品情報
# product_name = product.name # 購入した商品名
# amount = session["amount_total"] # 購入金額(手数料抜き)
#
# # DBに結果を保存
# SaveTransaction(product_name, customer_name, customer_email, amount)
#
#
# # 決済完了後メール送信(Djangoのメール機能利用)
# send_mail(
# subject = '商品購入完了!', # 件名
# message = '{}様\n商品購入ありがとうございます。購入された商品URLはこちら{}'.format(customer_name,product.url), # メール本文
# recipient_list = [customer_email], # TO
# from_email = 'test@test.com' # FROM
# )
# # 結果確認
# print(session)
#
# return HttpResponse(status=200)
顧客の商品決済完了イベントは以下に示すcheckout.session.completed
をもとに検知しています。
このイベントをコードで指定することで、決済時の顧客情報(顧客名・メールアドレス・購入商品・支払額など)を取得できるようになります。
# イベントハンドラ
@csrf_exempt
def stripe_webhook(request):
# サーバーのイベントログからの出力ステートメント
# 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:
# # 有効でないpayload
# return HttpResponse(status=400)
# except stripe.error.SignatureVerificationError as e:
# # 有効でない署名
# return HttpResponse(status=400)
# checkout.session.completedイベント検知
if event['type'] == 'checkout.session.completed':
session = event['data']['object']
# イベント情報取得
customer_name = session["customer_details"]["name"] # 顧客名
customer_email = session["customer_details"]["email"] # 顧客メール
product_id = session["metadata"]["product_id"] # 購入商品ID
product = Product.objects.get(id=product_id) # 購入商品情報
product_name = product.name # 購入した商品名
amount = session["amount_total"] # 購入金額(手数料抜き)
# # DBに結果を保存
# SaveTransaction(product_name, customer_name, customer_email, amount)
#
#
# # 決済完了後メール送信(Djangoのメール機能利用)
# send_mail(
# subject = '商品購入完了!', # 件名
# message = '{}様\n商品購入ありがとうございます。購入された商品URLはこちら{}'.format(customer_name,product.url), # メール本文
# recipient_list = [customer_email], # TO
# from_email = 'test@test.com' # FROM
# )
# # 結果確認
# print(session)
#
# return HttpResponse(status=200)
checkout.session.completed
で取得したイベント情報はSaveTransaction()
関数をもとにDBに登録されます。
また、決済完了に併せてメールで情報通知する設定を以下のコードで行なっています。
# イベントハンドラ
@csrf_exempt
def stripe_webhook(request):
# サーバーのイベントログからの出力ステートメント
# 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:
# # 有効でないpayload
# return HttpResponse(status=400)
# except stripe.error.SignatureVerificationError as e:
# # 有効でない署名
# return HttpResponse(status=400)
# checkout.session.completedイベント検知
# if event['type'] == 'checkout.session.completed':
# session = event['data']['object']
#
# # イベント情報取得
# customer_name = session["customer_details"]["name"] # 顧客名
# customer_email = session["customer_details"]["email"] # 顧客メール
# product_id = session["metadata"]["product_id"] # 購入商品ID
# product = Product.objects.get(id=product_id) # 購入商品情報
# product_name = product.name # 購入した商品名
# amount = session["amount_total"] # 購入金額(手数料抜き)
# DBに結果を保存
SaveTransaction(product_name, customer_name, customer_email, amount)
# 決済完了後メール送信(Djangoのメール機能利用)
send_mail(
subject = '商品購入完了!', # 件名
message = '{}様\n商品購入ありがとうございます。購入された商品URLはこちら{}'.format(customer_name,product.url), # メール本文
recipient_list = [customer_email], # TO
from_email = 'test@test.com' # FROM
)
# 結果確認
print(session)
return HttpResponse(status=200)
※本番環境用でのメール送信設定については、本記事の趣旨と外れるため割愛しています。
メール送信設定について詳しく知りたい方はDjangoメール送信ドキュメント をご覧ください。
SaveTransaction関数について
後述するmodels.pyにて顧客の購入履歴を管理するトランザクションマスタ を作成します。そのマスタに対してレコードを挿入するための関数がSaveTransaction
という位置付けです。こちらを用いて顧客の購入イベントをDB上で管理していきます。
# 顧客の商品購入履歴を保存
def SaveTransaction(product_name, customer_name, customer_email, amount):
# DB保存
saveData = Transaction.objects.get_or_create(
product_name = product_name,
date = datetime.datetime.now(),
customer_name = customer_name,
email = customer_email,
product_amount = amount
)
return saveData
画面の表示設定|urls.py
Project_Folder > urls.py
を開き、webhookのurlを追加します。以下のコードに書き換えましょう。
from django.contrib import admin
from django.urls import path
from django.conf import settings # settings.pyの変数
from django.conf.urls.static import static # メディア表示
# App_Folderからviews.pyで定義した関数呼出
from App_Folder.views import (
CreateCheckoutSessionView,
ProductTopPageView,
SuccessPageView,
CancelPageView,
stripe_webhook,
)
urlpatterns = [
path('admin/', admin.site.urls), # 管理画面
path("", ProductTopPageView.as_view(), name="product-top-page"), # 商品トップ
path("create-checkout-session/<pk>/", CreateCheckoutSessionView.as_view(), name="create-checkout-session"), # 個別商品決済画面
path("success/", SuccessPageView.as_view(), name="success"), # 決済成功時にリダイレクト先
path("cancel/", CancelPageView.as_view(), name="cancel"), # 決済キャンセル時のリダイレクト先
path("webhook/", stripe_webhook, name="webhook"), # 追加 Webhook
]
# メディア表示
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Webhookのシークレットキーを取得
Stripeに「このPCのlocalhost:8000/webhook/
に情報を転送して」という命令を伝える作業を行います。このようにローカルのURLを設定するとこで、イベントハンドラが取得したイベント情報をローカルPCから簡単に確認することができるようになります。
前述でCLIにログインしたインタラクティブシェルに対して以下を入力し実行しましょう。
stripe listen --forward-to localhost:8000/webhook/
上記を入力すると、次のような出力結果が得られます。その中にWebhookシークレットキー が表示されていますため、お手元にメモしましょう。
Ready! You are using Stripe API Version [2020-08-27].
Your webhook signing secret is 'xxxxx WEBHOOKシクレットキーxxxxxx' (^C to quit)
Webhookシークレットキーを登録|settings.py
前述で取得したWebhookのシークレットキーをsettings.pyに追記します。以下の部分を更新しましょう。
# Stripeのパブリックキー
STRIPE_PUBLIC_KEY = 'ここにSTRIPE_PUBLIC_KEYを入力'
# Stripeのシークレットキー
STRIPE_SECRET_KEY = 'ここにSTRIPE_SECRET_KEYを入力'
# StripeのWebhookのシークレットキー
STRIPE_WEBHOOK_SECRET = 'ここにSTRIPE_WEBHOOK_SECRETを入力'
データベース設定|models.py・admin.py
Stripeオンライン決済画面で商品を購入した顧客のイベントを検知し、Django DBに情報を保存するための設定を以下行っていきます。
① models.pyにトランザクションマスタを追加 する
App_Folde > models.pyを開き、商品購入履歴を管理するトランザクションマスタを作成します。以下のコードを追記しましょう。
コード(トランザクションマスタ)
from django.db import models
# 商品マスタ
class Product(models.Model):
# 商品名
name = models.CharField(max_length=100)
# 商品概要
description = models.CharField(max_length=255, blank=True, null=True)
# 商品ID(★stripeの商品IDを値に用いる)
stripe_product_id = models.CharField(max_length=100)
# 商品写真登録用のファイル
file = models.FileField(upload_to="product_files/", blank=True, null=True)
# 商品詳細ページのリンク
url = models.URLField()
# admin画面で商品名表示
def __str__(self):
return self.name
# 価格マスタ
class Price(models.Model):
# 外部キーで商品マスタを紐付け
product = models.ForeignKey(Product, related_name='Prices', on_delete=models.CASCADE)
# 価格ID(★stripeの価格IDを値に用いる)
stripe_price_id = models.CharField(max_length=100)
# 価格
price = models.IntegerField(default=0)
# Django画面に表示する価格
def get_display_price(self):
return self.price
# トランザクションマスタ
class Transaction(models.Model):
# 購入日
date = models.CharField(max_length=100)
# 購入者
customer_name = models.CharField(max_length=100)
# 購入者のメールアドレス
email = models.EmailField(max_length=100)
# 購入商品名
product_name = models.CharField(max_length=100)
# 支払い金額
product_amount = models.IntegerField()
# admin画面で商品名表示
def __str__(self):
return self.date + '_' + self.product_name + '_' + self.customer_name
テーブル概要(トランザクションマスタ)
トランザクションマスタの項目概要は次のとおりです。
項目名 概要 date 商品購入日 customer_name 商品の購入者 email 商品の購入者のメールアドレス product_name 購入された商品 product_amount お支払い金額
② マイグレーションを行い、DBにテーブル情報を適用する
Django DB(SQLite3)にトランザクションテーブルを作成します。
(1) インタラクティブシェルを開き、manage.py
のあるディレクトリまで移動しましょう。
そして、以下を入力しマイグレーションを実行します。
python3 manage.py makemigrations
(2) 続いて、以下のコードを入力・実行します。
python3 manage.py migrate
④ admin.pyを設定しトランザクションテーブル情報を確認する
Djangoの管理画面から前述で作成したトランザクションテーブルが閲覧できるように、admin.pyを修正します。App_Folder > admin.py
を開き、以下のコードを記述しましょう。
from django.contrib import admin
from .models import Product, Price, Transaction # models.pyで作成したマスタを読込
class PriceInlineAdmin(admin.TabularInline):
model = Price
extra = 0
class ProductAdmin(admin.ModelAdmin):
inlines = [PriceInlineAdmin]
# 管理画面に商品マスタと価格マスタを表示
admin.site.register(Product, ProductAdmin)
admin.site.register(Price)
admin.site.register(Transaction)
動作確認
ターミナルまたはコマンドプロンプト上で商品支払い履歴を確認
Stripe決済画面からテスト商品を購入してみましょう。
購入後、「LCIにログインしたインタラクティブシェル 」と「ローカルサーバーを立ち上げたインタラクティブシェル 」それぞれの出力結果で以下が得られていれば、正常に動作していることを意味します。
Stripe LCIにログインしたインタラクティブシェル
商品購入時に、以下のようなHTTP Response=200
が得られていれば問題なしです。
2022-07-30 20:03:48 --> payment_intent.created [evt]
2022-07-30 20:03:48 <-- [200] POST http://localhost:8000/webhook/ [evt]
2022-07-30 20:04:40 --> payment_intent.created [evt]
2022-07-30 20:04:40 <-- [200] POST http://localhost:8000/webhook/ [evt]
2022-07-30 20:05:03 --> customer.created [evt_1LRDTfH3fFMKpklidMFvNV2O]
2022-07-30 20:05:03 <-- [200] POST http://localhost:8000/webhook/ [evt]
2022-07-30 20:05:03 --> payment_intent.succeeded [evt]
2022-07-30 20:05:03 <-- [200] POST http://localhost:8000/webhook/ [evt]
2022-07-30 20:05:03 --> charge.succeeded [evt_3LRDTIH3fFMKpkli14GuP26O]
2022-07-30 20:05:03 <-- [200] POST http://localhost:8000/webhook/ [evt]
2022-07-30 20:05:03 --> checkout.session.completed [evt_1LRDTfH3fFMKpkli7mSB9vPt]
2022-07-30 20:05:04 <-- [200] POST http://localhost:8000/webhook/ [evt]
ここでレスポンスエラーが発生した場合、前述のstripe listen --forward-to localhost:
で正しくURLが設定されているかをまず疑いましょう。
それでも解消しない場合は「views.pyのイベントハンドラ関数のコードミス」または「urls.pyにwebhookのurlが正しく設定されていない」可能性があります。
ローカルサーバーを立ち上げたインタラクティブシェル
商品購入時に、Stripeから以下のようなJSONレスポンスが受信できていれば正常な動作と判断できます。このレスポンスはcheckout.session.completed
イベント検知時の出力結果になります。
{
"after_expiration": null,
"allow_promotion_codes": null,
"amount_subtotal": 1000,
"amount_total": 1000,
"automatic_tax": {
"enabled": false,
"status": null
},
"billing_address_collection": null,
"cancel_url": "http://127.0.0.1:8000/cancel/",
"client_reference_id": null,
"consent": null,
"consent_collection": null,
"currency": "jpy",
"customer": "cus_xxxxxx",
"customer_creation": "always",
"customer_details": {
"address": {
"city": null,
"country": "JP",
"line1": null,
"line2": null,
"postal_code": null,
"state": null
},
"email": "a@test.com",
"name": "f",
"phone": null,
"tax_exempt": "none",
"tax_ids": []
},
"customer_email": null,
"expires_at": 16511111,
"id": "cs_test_xxxxxx",
"livemode": false,
"locale": null,
"metadata": {
"product_id": "1"
},
"mode": "payment",
"object": "checkout.session",
"payment_intent": "pi_xxxxxxxx",
"payment_link": null,
"payment_method_options": {},
"payment_method_types": [
"card"
],
"payment_status": "paid",
"phone_number_collection": {
"enabled": false
},
"recovered_from": null,
"setup_intent": null,
"shipping": null,
"shipping_address_collection": null,
"shipping_options": [],
"shipping_rate": null,
"status": "complete",
"submit_type": null,
"subscription": null,
"success_url": "http://127.0.0.1:8000/success/",
"total_details": {
"amount_discount": 0,
"amount_shipping": 0,
"amount_tax": 0
},
"url": null
}
さらに、次のようなテストメール送信通知 もコマンドプロンプト上に表示されれば正常動作との判断可能です。
Djangoのメール送信機能については今回の趣旨と外れるため、具体的な解説は割愛します。
XXXX様
商品購入ありがとうございます。購入された商品URLはこちらhttps://di-acc2.com
Django DBで商品支払い履歴を確認
最後に、Django管理画面を開きTransactionsテーブルの中身 を確認しましょう。
上記のように、Stripeオンライン決済画面で決済完了した履歴がレコードとして登録されていれば正常動作と判断します。
【まとめ】Stripe×Djangoでオンライン決済機能の導入
これにてStripeのオンライン決済機能をDjangoで導入するプロセスは全て完了です!
本番運用を見越したアプリ実装は、APIキーを本番用に書き換え、商品も本番用に登録し直すだけで簡単に対応することができます。
【参考】本記事で紹介したコードのgithubレポジトリ
本記事で紹介したコードは以下のgithubレポジトリに格納しています。適時参照下さい。
Githubリンク:https://github.com/dxcelwave/django-stripe-onlinepayment/tree/main
【参考】Djangoの解説記事一覧
最後までご覧いただきありがとうございました。当サイトではDjangoフレームワークを用いた解説記事を多数取り扱っております。次のように体系的に整理しておりますため学習にお役立て下さい。
Django学習に最適!
Django学習に最適!おすすめ教材一覧
当サイトではDjangoを詳しく学習したい方向けに、筆者も利用したおすすめの教材を紹介しています。
当サイトが運営するDjango記事一覧
Djangoをもっと詳しく!解説記事一覧
当サイトではDjangoの解説記事を多数配信しています。体系的に学べるようになっておりますため、是非ご覧下さい。
【参考】Pythonでできること・お仕事探し
Pythonでできること・副業案件の探し方
「Pythonで実現できるお役立ち情報」を多数配信中!Python習熟者向けに「おすすめのPython副業・フリーランス案件の探し方」についてもご紹介してます。