【Stripe】Djangoで作成したWebアプリにサブスクリプションを追加する

webアプリに欲しい機能として決済機能が挙げられます。
今回はstripeで実装してみました。

## 実現したいこと
- Djangoにサブスクリプション機能を追加する

## 前提条件
- Djangoチュートリアルが完了していること

## 作業環境
- OS
    - CentOS7
- webサーバー
    - Nginx
- 言語
    - Python
- フレームワーク
    - Django

## 参考サイト
- [Django Stripe Subscriptions](https://testdriven.io/blog/django-stripe-subscriptions/#authentication)
- [Create subscriptions with Checkout](https://stripe.com/docs/billing/subscriptions/checkout)

## 手順
1. プロジェクト作成＆アプリ作成
2. stripeインストール
3. カスタマーモデル追加
4. サブスクリプションプラン作成
5. チェックアウトセッション作成
6. 支払いを行う

### 1. プロジェクト作成＆アプリ作成
まずは今回使うライブラリをインストールしていきます。
ライブラリの移動がしやすいようにrequirements.txtを作成します

```terminal
touch requirements.txt
```

今回使うライブラリを記述します。

```requirements.txt
Django==3.1.4
stripe==2.56.0
```

ライブラリをインストールします。

```terminal
pip install -r requirements.txt
```

プロジェクトを作成していきます。
django_stripeはプロジェクト名ですので、なんでもいいです。
同じにした方が後々分かりやすいと思います。

```terminal
django-admin startproject django_stripe
```

アプリを追加します。

```terminal
python manage.py startapp billing #請求って意味の英単語です
```

ここまでの作成で作業フォルダは以下のようになります。

```terminal
.
├── billing
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── __init__.cpython-39.pyc
│   │   ├── admin.cpython-39.pyc
│   │   └── models.cpython-39.pyc
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   │   ├── __init__.py
│   │   └── __pycache__
│   │       └── __init__.cpython-39.pyc
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── db.sqlite3
├── django_stripe
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── __init__.cpython-39.pyc
│   │   ├── settings.cpython-39.pyc
│   │   ├── urls.cpython-39.pyc
│   │   └── wsgi.cpython-39.pyc
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py
└── requirements.txt
```

### 2. アプリインストール
インストールドアプリに先ほど作成したbillingアプリを追加します。

```django_stripe/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'billing', #追加
]
```

### 3. カスタマーモデル追加
stripeで使うモデルを作成します。

```billing/models.py
from django.db import models
from django.contrib.auth.models import AbstractBaseUser
from django.utils import timezone


class User(AbstractBaseUser):
    email = models.EmailField('メールアドレス', unique=True)
    password = models.CharField('パスワード',max_length=30)
    first_name = models.CharField('性', max_length=30)
    last_name = models.CharField('名', max_length=30)
    address = models.CharField('住所', max_length=30, blank=True)
    tel = models.CharField('電話番号', max_length=30, blank=True)
    created = models.DateTimeField('入会日',default=timezone.now)

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
```

### 4. サブスクリプションプラン作成

次はstripeでサブスクリプションのプランを作成していきます。
プランの作成方法は2種類あります。
・CLI
・公式サイト

今回は作成が簡単な公式サイトからプランを作成していきます。
以下のリンクからアクセスしてお好みのプランを作成してください。
https://dashboard.stripe.com/test/products/create

作成した後はAPI IDを忘れないようにメモしておきます。
後で必要になります。

### 5. チェックアウトセッション作成
次は支払い画面を作成していきます。
APIに必要なキーは以下から取得できます。
https://dashboard.stripe.com/test/apikeys

```django_stripe/settings.py
# STRIPE 追加
STRIPE_PUBLIC_KEY = '公開可能キー'
STRIPE_SECRET_KEY = 'シークレットキー'
STRIPE_PRICE_ID = 'API_ID　price_xxxxなやつ'
```

```billing/views.py
from django.shortcuts import render
from django.conf import settings
from django.http.response import JsonResponse, HttpResponse
from django.views.decorators.csrf import csrf_exempt
from .models import User, StripeCustomer
from django.contrib.auth.decorators import login_required
import stripe

# 設定用の処理
@csrf_exempt
def stripe_config(request):
    if request.method == 'GET':
        stripe_config = {'publicKey': settings.STRIPE_PUBLIC_KEY}
        return JsonResponse(stripe_config, safe=False)

# 支払い画面に遷移させるための処理
@csrf_exempt
def create_checkout_session(request):
    if request.method == 'GET':
        domain_url = 'http://localhost:8000/billing/'
        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)})

# 支払いに成功した後の画面
def success(request):
    return render(request, 'billing/success.html')

# 支払いに失敗した後の画面
def cancel(request):
    return render(request, 'billing/cancel.html')
```

この処理を呼び出すためのURLを作成します。

```billing/urls.py
from django.urls import path
from accounts import views
from django.contrib import admin
from billing import views


urlpatterns = [
    path('', views.index, name='index'),
    path('config/', views.stripe_config),
    path('create-checkout-session/', views.create_checkout_session),
    path('success/', views.success),
    path('cancel/', views.cancel),
]
```

テンプレートを作成します。

```billing/templates/billing/index.html
{% load static %}
<head>
  <title>Checkout</title>
  <script src="https://js.stripe.com/v3/"></script>
  <script src="{% static 'billing/js/stripe.js' %}"></script>
</head>
<body>
  <button id="checkout">Subscribe</button>
</body>
```

```billing/templates/billing/success.html
success
```

```billing/templates/billing/cancel.html
cancel
```

javascriptの処理を記述します。

```billing/static/billing/js/stripe.js
console.log("Sanity check!");

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

        // Event handler
        let submitBtn = document.querySelector("#checkout");
        if (submitBtn !== null) {
            submitBtn.addEventListener("click", () => {
                // Get Checkout Session ID
                fetch("/billing/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);
                    });
            });
        }
    });
```

最終的なファイル構成は以下の通りです。

```
.
├── billing
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── __init__.cpython-39.pyc
│   │   ├── admin.cpython-39.pyc
│   │   ├── models.cpython-39.pyc
│   │   ├── urls.cpython-39.pyc
│   │   └── views.cpython-39.pyc
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   │   ├── 0001_initial.py
│   │   ├── __init__.py
│   │   └── __pycache__
│   │       ├── 0001_initial.cpython-39.pyc
│   │       └── __init__.cpython-39.pyc
│   ├── models.py
│   ├── static
│   │   └── billing
│   │       └── js
│   │           └── stripe.js #追加
│   ├── templates
│   │   └── billing
│   │       ├── cancel.html #追加
│   │       ├── index.html #追加
│   │       └── success.html #追加
│   ├── tests.py
│   ├── urls.py
│   └── views.py
├── db.sqlite3
├── django_stripe
│   ├── __init__.py
│   ├── __pycache__
│   │   ├── __init__.cpython-39.pyc
│   │   ├── settings.cpython-39.pyc
│   │   ├── urls.cpython-39.pyc
│   │   └── wsgi.cpython-39.pyc
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py
└── requirements.txt
```
### 6. 支払いを行う
サーバーを起動させます

```
python manage.py runserver
```

(http://127.0.0.1:8000/billing)
アクセスするとボタンだけの画面になります。
ボタンをクリックするとstripeが用意してくれている支払い画面に遷移します。

後はテスト用のクレジットカード番号 4242 4242 4242 4242を入力します。

するとsuccessと表示されるページに遷移します。
これでサブスクリプションが完了になります。

#### 質問や間違いなどありましたらコメントでお願いします。

## 参考サイト
- [Django Stripe Subscriptions](https://testdriven.io/blog/django-stripe-subscriptions/#authentication)
- [Create subscriptions with Checkout](https://stripe.com/docs/billing/subscriptions/checkout)