Python(Django)でStripeによる決済システムを導入する①
株式会社 var でエンジニアインターンをしているひらはらです。
本記事では、決済サービスであるStipeをサイト内へ導入したので紹介します。
背景
弊社では RareTECH という IT スクールを運営しており、その中で受講生の学習が円滑に進むように過去授業の閲覧や講義日程、受講生の一覧情報等を確認できるユーザー管理サイト(ポータルサイト)を提供しています。
これまで入会する方には Line から PayPal の決済リンクへ飛んでもらい決済をして頂いていましたが、Line への登録・決済・ユーザー管理サイトへの登録等、フローが複雑になりつつありました。そこで、ユーザー管理サイトで一括でサイトへの登録とRareTECHでの決済を完結させるために、ユーザー管理サイトへ決済システムの導入を決めました。
また、どの決済サービスを導入するかについては、弊社で開発している Envader の決済で既に Stripe を使用している経緯もあり、Stripe 決済へ移行する運びとなりました。
Stripe とは
Stripe は決済プラットフォームの一つです。ユーザーと事業者間の決済を仲介してくれます。決済手数料は 3.6%と低額で、Stripe を導入するためのドキュメントも豊富ということでエンジニアから高い評価があります。
実装
弊社が運営するITスクールである RareTECH では、入会時のみの入会金と定期支払いとして月額もしくは年額のサブスクリプションの 2 つの決済を用意しています。そのため入会金ではCheckout
、サブスクリプションではBilling
という Stripe のサービスを使用しました。
バックエンドではStripeAPI
をpythonで使用し、フロントエンドではStripeのJavaScriptライブラリであるStripe.js
を使用しています。
技術
ユーザー管理サイトでは Django で開発しており、Django を使用しました。また、フロントでは jQuery を使用しています。
- Django
- jQuery
- MySQL
一度きり決済の作成
細かいコードは省きましたが、stripe checkout では概ね次のような実装です。
ユーザーが入会金を支払うボタンをクリックした際、create_checkout_session
が呼ばれ、決済のセッションを作成し sessionid
をフロントに json
で返します。フロントでは sessionid
を受け取り、Stripe が提供する決済ページへリダイレクトさせています。
ポイントは次のとおりです。
-
mode
をpayment
に設定 -
line_items
の中のprice
で商品の ID を指定 - ユーザーのメールアドレス入力を省略するために ポータルサイトで登録している
customer_email
を指定 -
success_url
に決済が成功したときの URL とCHECKOUT_SESSION_ID
を指定 - フロントに
json
でsessionid
を返却
@require_POST
@csrf_exempt
def create_checkout_session(request):
user = request.user
price_id = json.loads(request.body)
email = request.user.get_email()
try:
checkout_session = stripe.checkout.Session.create(
payment_method_types=['card'],
line_items=[
{
'price': price_id,
'quantity': 1,
},
],
mode='payment',
customer_email=email,
allow_promotion_codes=True,
success_url=DOMAIN + 'subscription?session_id={'
'CHECKOUT_SESSION_ID}',
cancel_url=DOMAIN + 'checkout'
)
return JsonResponse({'id': checkout_session.id})
btn.on("click", () => {
checkout();
});
const checkout = () => {
const STRIPE_PUBLIC_KEY = $("#stripe-public-key").val();
const stripe = Stripe(STRIPE_PUBLIC_KEY);
const value = $("input.admission").val();
fetch("/create_checkout_session/", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json; charset=UTF-8",
"X-CSRFToken": "{{ csrf_token }}",
},
body: JSON.stringify(value),
}).then((res) =>
res
.json()
.then((session) => {
stripe.redirectToCheckout({ sessionId: session.id });
})
.catch((err) => console.log(err))
);
};
一度きり決済成功時の DB への保存
先程の入会金決済が成功するとsuccess_url
で指定したページに遷移します。その際、URLのクエリーとしてCHECKOUT_SESSION_ID
も返るため、python の request.GET
でsessionid
として取得します。その後、sessionid
からセッションオブジェクトを取得し、必要なデータを DB へ保存しました。
session_id = request.GET.get('session_id')
session = stripe.checkout.Session.retrieve(session_id)
customer_id = session['customer']
admission_id = session['payment_intent']
サブスクリプションの作成
サブスクリプションでも先程と同様ですが、mode
へsubscription
を指定しています。また、stripe checkout の入会金決済時に DB へ customer_id
を保存したため、こちらでは customer_id
を指定しています。
@require_POST
@csrf_exempt
def create_subscription_session(request):
user = request.user
price_id = json.loads(request.body)
customer_id = request.user.get_customer_id()
try:
checkout_session = stripe.checkout.Session.create(
payment_method_types=['card'],
line_items=[
{
'price': price_id,
'quantity': 1,
},
],
mode='subscription',
allow_promotion_codes=True,
client_reference_id=customer_id,
customer=customer_id,
success_url=DOMAIN + 'slack?session_id={'
'CHECKOUT_SESSION_ID}',
cancel_url=DOMAIN + 'subscription'
)
return JsonResponse({'id': checkout_session.id})
btn.on("click", () => {
const value = $('input.input-radio[type="radio"]:checked').val();
subscription(stripe, value);
});
const subscription = (stripe, value) => {
fetch("/create_subscription_session/", {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json; charset=UTF-8",
"X-CSRFToken": "{{ csrf_token }}",
},
body: JSON.stringify(value),
})
.then((res) => {
return res.json();
})
.then((session) => {
stripe.redirectToCheckout({ sessionId: session.id });
})
.catch((err) => console.error(err));
};
サブスクリプション成功時の DB への保存
こちらも先ほどと同様に URL からsession_id
のみを取得し、必要なデータを取得して DB へ保存しました。
session = request.GET.get('session_id')
stripe_session = stripe.checkout.Session.retrieve(session)
subscription_id = session['subscription']
まとめ
今回は Django での Stripe 導入について記載しました。
実装自体は諸々含めて1週間もかかっていなかったと思います。実際にStripe自体も公式ドキュメントが豊富で、サンプルコードとStripeAPIドキュメントを読み込みながらDjangoに当てはめていく作業でした。
難しかった点を挙げると、JavaScript の理解はありましたがPython と Django をあまり触ったことがなかったため、Sripe よりも Django のコードを書く要領が悪かったなという印象です。
しかし、今回を通してStripe決済を実装する方法は概ね理解できたので良かったです。
作成以降の管理として webhook 関連の処理については別の記事でまとめたいと思います。
更新
Webhookについての記事を下記のリンクで記載しています。
宣伝
弊社では、インフラ学習サイト Envader(エンベーダー)と IT スクール(RareTECH)を運営しています。また、企業研修やシステム開発なども行っていますので興味がある方は HP よりご連絡下さい。
Discussion