Djangoで簡単なショッピングサイトを作る(Stripe決済)
概要
決済処理をWebアプリケーションに組み込みたい場合がよくあります。今回はDjango
とStripe
を使い、決済処理の簡単なサンプルを作っていきます。
Stripeを利用する準備
まずですが、Stripe
を利用するためにユーザー登録しましょう。
登録が済んだら、ダッシュボードへいきましょう。開発者→APIキーと開き、テスト用キーを表示ボタンを押し、2つのキーを表示しておきます。
公開可能と、シークレットキーの2つですね。本番環境利用の申請をするまでは、テスト用のキーが表示されています。
Djangoのsettings.py
にて、以下のように定義しておきます。
STRIPE_PUBLIC_KEY = 'あなたの公開可能キー'
STRIPE_SECRET_KEY = 'こっちはシークレットキー'
Stripe
を利用するためのPythonライブラリがあります。pipでインストールしておきましょう。
pip install stripe
私は本が好きなので、オンライン書店をすることにしました。以下のようなmodels.py
をサンプルに進めていきます。
from django.conf import settings
from django.db import models
from django.utils import timezone
class Book(models.Model):
"""本"""
title = models.CharField('タイトル', max_length=200)
price = models.IntegerField(default=1000)
description = models.TextField('説明')
created_at = models.DateTimeField('日付', default=timezone.now)
def __str__(self):
return self.title
class BuyingHistory(models.Model):
"""購入履歴"""
book = models.ForeignKey(Book, verbose_name='購入書籍', on_delete=models.PROTECT)
user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='購入ユーザー', on_delete=models.PROTECT)
is_sended = models.BooleanField('発送フラグ', default=False)
stripe_id = models.CharField('タイトル', max_length=200)
created_at = models.DateTimeField('日付', default=timezone.now)
def __str__(self):
return '{} {} {}'.format(self.book, self.user.email, self.is_sended)
Book
モデルは簡単ですね。タイトルと値段、本の説明等のフィールドを持ちます。Stripeでは日本円での決済ができるので単純なIntegerField
にしましたが、使用通貨によっては小数点部分が必要になりますので、その際はDecimalField
等を使いましょう。
BuyingHistory
モデルはユーザーの購入履歴です。購入書籍、購入ユーザー、発送済みフラグ、そしてstripe_idというフィールドを持たせています。Stripeには豊富なAPIや解りやすい管理画面があるので、そちらでも履歴は確認できますが、Django側にも履歴を残したかったので作成したモデルです。
決済処理のサンプル
実際に使っていきます。まずviews.py
です。
from django.conf import settings
from django.shortcuts import redirect, render
from django.views import generic
from .models import Book, BuyingHistory
import stripe
stripe.api_key = settings.STRIPE_SECRET_KEY
class IndexView(generic.ListView):
model = Book
class DetailView(generic.DetailView):
model = Book
def post(self, request, *args, **kwargs):
"""購入時の処理"""
book = self.get_object()
token = request.POST['stripeToken'] # フォームでのサブミット後に自動で作られる
try:
# 購入処理
charge = stripe.Charge.create(
amount=book.price,
currency='jpy',
source=token,
description='メール:{} 書籍名:{}'.format(request.user.email, book.title),
)
except stripe.error.CardError as e:
# カード決済が上手く行かなかった(限度額超えとか)ので、メッセージと一緒に再度ページ表示
context = self.get_context_data()
context['message'] = 'Your payment cannot be completed. The card has been declined.'
return render(request, 'app/book_detail.html', context)
else:
# 上手く購入できた。Django側にも購入履歴を入れておく
BuyingHistory.objects.create(book=book, user=request.user, stripe_id=charge.id)
return redirect('app:index')
def get_context_data(self, **kwargs):
"""STRIPE_PUBLIC_KEYを渡したいだけ"""
context = super().get_context_data(**kwargs)
context['publick_key'] = settings.STRIPE_PUBLIC_KEY
return context
Stripeの使い方としては、stripe.api_key
にシークレットキーを設定し、各種APIを叩いていくことになります。ここはおまじないと思ってしまってください。
import stripe
stripe.api_key = settings.STRIPE_SECRET_KEY
書籍の一覧を表示するビューです。特に言うことはないですね。
class IndexView(generic.ListView):
model = Book
book_list.html
は、以下のようになります。これも言うことはありません。一覧を表示して、詳細ページへのリンクを作るだけです。
{% extends 'app/base.html' %}
{% block content %}
<h1>書籍一覧</h1>
{% for book in book_list %}
<p><a href="{% url 'app:detail' book.pk %}">{{ book.title }}</a></p>
{% endfor %}
{% endblock %}
詳細ページのビューは少し複雑になったので、機能毎に説明していきます。まず、書籍の詳細を表示する部分です。
PUBLIC_KEY
をテンプレートに渡す必要がありまして、get_context_data
メソッドを上書きして渡すことにしています。context_processors
を使ったり、テンプレートにPUBLIC_KEY
を直接書いてもいいかもしれません。
class DetailView(generic.DetailView):
model = Book
def get_context_data(self, **kwargs):
"""STRIPE_PUBLIC_KEYを渡したいだけ"""
context = super().get_context_data(**kwargs)
context['publick_key'] = settings.STRIPE_PUBLIC_KEY
return context
そして、購入処理もこの詳細表示ビューで行います。もちろん、専用のビューを作っても良いでしょう。処理内容はコメントのとおりです。
def post(self, request, *args, **kwargs):
"""購入時の処理"""
book = self.get_object()
token = request.POST['stripeToken'] # フォームでのサブミット後に自動で作られる
try:
# 購入処理
charge = stripe.Charge.create(
amount=book.price, # 値段
currency='jpy', # 日本円
source=token,
description='メール:{} 書籍名:{}'.format(request.user.email, book.title),
)
except stripe.error.CardError as e:
# カード決済が上手く行かなかった(限度額超えとか)ので、メッセージと一緒に再度ページ表示
context = self.get_context_data()
context['message'] = 'Your payment cannot be completed. The card has been declined.'
return render(request, 'app/book_detail.html', context)
else:
# 上手く購入できた。Django側にも購入履歴を入れておく
BuyingHistory.objects.create(book=book, user=request.user, stripe_id=charge.id)
return redirect('app:index')
book_detail.html
は、以下のようにしました。ログインしていないと、購入用のボタンを見えなくしています。
{% extends 'app/base.html' %}
{% block content %}
<p>{{ message }}</p>
<a href="{% url 'app:index' %}">戻る</a>
<!-- 書籍の情報の表示 -->
<h1>{{ book.title }}</h1>
<p class="lead">{{ book.created_at }} - {{ book.price }}円</p>
<p>{{ book.description | linebreaksbr }}<p>
<!-- 購入ボタン・フォームの作成 -->
{% if user.is_authenticated %}
<form action="" method="POST">
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="{{ publick_key }}"
data-amount="{{ book.price }}"
data-name="なりと書店"
data-description="{{ book.title }}"
data-image="https://stripe.com/img/documentation/checkout/marketplace.png"
data-locale="ja"
data-currency="jpy"
data-email="{{ user.email }}">
</script>
{% csrf_token %}
</form>
{% endif %}
{% endblock %}
data-locale
はautoでも大丈夫だと思いますが、念の為jaとしました。data-currency
も円にし、今回のようにユーザーのメールアドレスがわかっている場合はdata-email
属性に指定します。わからなければ、data-email属性は消しましょう。
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="{{ publick_key }}"
data-amount="{{ book.price }}"
data-name="なりと書店"
data-description="{{ book.title }}"
data-image="https://stripe.com/img/documentation/checkout/marketplace.png"
data-locale="ja"
data-currency="jpy"
data-email="{{ user.email }}">
</script>
では、実際に動くか試していきます。本番ではhttpsからのアクセスでないと弾かれるようですが、登録した後のテスト状態ではhttpでも大丈夫なようです。
まずトップページ
詳細ページに行くと、Pay with Card というボタンができていますね。
クリックして、決済情報を入力します。この4242...は、テスト用のカードです。
購入が終わったら、stripeの管理画面に行きます。支払いをクリックすると、購入されたものが表示されます。
金額や説明を確認できます。
Django側のBuyingHistory
も見てみましょう。
購入ユーザーや購入書籍、stripe_id(タイトルと表示されちゃってますね...)を上手く作れました。
stripe_idはDetailViewのpostメソッド内にて、送信後にcharge.id
として取得したもので、stripeの管理画面で表示されたIDと同じものになります。これを格納しておくと、Django⇔stripe間でのデータの紐付けも簡単にできるでしょう。
ユーザーのメールアドレスを元に受け渡しの連絡を行ったり、住所となるフィールドを持たせた拡張Userモデル等を作れば発送もできるようになるでしょうし、電子コンテンツの場合は購入済みならダウンロードさせる、といったことができそうです。