【Python】Stripe+FlaskでWebサイトを作る


【Python】Stripe+FlaskでWebサイトを作る

Webサイトで モノを売買したり、ファイルを提供したりする際に "お金の決済" が必要な場合がありますよね。 一般的には何らかの決済サービスを利用すると思いますが、 手数料や維持費、着金までのタイムラグなどの難点も...

手数料は以前に比べると下がったものの 5% ほど掛かることもありますし、 売上がなくても月額最低手数料 が2,000円 かかるサイトもあります。

こうした Web上での 決済問題は、 『Stripe』 というサービスと Webフレームワークを使えばいくらか改善できます。

今回は、 小規模な Webサイト を想定して、 Python の Flask をベースに Stripe で "デモ決済" を実行してみました。

本稿は、 Python の基礎習得済みの方を対象としています。
また記事内で使用するコードは、 Mac・ Linux に準拠したコードです。
Winddows の方は、参考画像を元に公式ドキュメントから Winddows用コードを参考の上、試してみてください。

【FlaskにStripe機能を搭載する様子の動画】

上記動画のコード: GitHub

目次
  1. 【Python】Stripe+Flaskで稼げるWebサイトを作る
  2. Stripeとは
  3. Stripeを使う時の注意点
  4. Stripeの基本的な使い方
  5. Stripeのカード入力画面を作る
  6. FlaskにStripeをセットする
  7. まとめ

【Python】Stripe+Flaskで稼げるWebサイトを作る

Stripeとは

デモ・モードで Stripe 決済を試している時の売上画面

Stripeは、 2010年に 2人のアイルランド人がはじめたオンライン決済サービスで、現在世界で 85万以上の Webサービスで利用され、国内では 440ほど使われています*。 "お金を扱う" というと、信用力が重要になってきますが、こちらの Stripe社、設立から 10年ほどなのに企業価値は 4兆円($35billion)ほど。日本で企業価値 4兆円 となると『伊藤忠商事』や 『みずほファイナンシャルグループ』レベルですので、これはおおむね信用できそうですよね(企業価値資料)。

Stripe の特徴としては、

  • 手数料 3.6%
  • カード決済OK
  • 出金は毎日でもOK
  • ストレスを感じにくい処理スピード
  • Apple Pay や Google Pay も OK
  • 定期課金(Subscription)もOK
  • Webへのサポート言語は、 PHP、 Ruby、 JavaScript、 Python etc
  • 見やすい管理画面
  • 決済完了メールや商品の送料関係の処理

とにかく安い決済手数料に加えて、柔軟な出金サポート、そして ネット通販"に対する充実のサポート機能まで。 Stripe が使えたら、もう代行決済は必要ありませんね。

Stripe は公式ドキュメントも充実しているので、すぐに使いこなせそうに思えましたが、実際に使ってみると注意点がいくつかあったのでご紹介します。

現在デフォルトの状態では、出金用口座を登録できません(9桁の金融番号が必要)。

Stripeを使う時の注意点

旧バージョンの Stripe で決済を行っている様子

  • SSL通信(https)必須
  • 旧バージョンと新バージョンがある
  • JavaScript や CSS のスキルも必須

Stripe は素晴らしい決済サービスですが、参考となる記事や動画が少ないです。あっても旧バージョンのスクリプトで実装されていたりと参考になりません。公式ドキュメントのボリュームは大きく、 GitHub にもサンプルがたくさん公開されていますが、情報にまとまりがなく混乱しやすいです。

まずは Stripe の基本的な動きを確認しておくと、このあとのプログラミングに役立つと思います。

Stripeの入力画面だけを用意した結果(ブラウザで確認


【Stripeの大まかな決済フロー】
① カードの入力画面を表示させるために、 Stripe その画面専用の JavaScript、 CSS が必要。
② 入力内容は、バックグランド処理にポストされて、そこでセットした価格が決済される
③ 合わせて Stripe の iframe が自動処理(iframe自体も自動生成)される
④ 決済完了すれば、支払い完了画面になる

上記画像、シンプルなカード情報入力画面ですが、これは <script src="https://js.stripe.com/v3/"> と CSS 、 HTML を使って生成した結果。簡単そうに見える Stripe の決済画面も、実はいくつもの要素が関係して成り立っていることをあらかじめ知っておく必要があるでしょう。

そして Stripe 決済を デモ or テスト しようと思った時、 SSL(https) でデータを通信する必要があります() 。 ローカル環境で簡単に SSL で Flask や Django 実行できないかテストしましたが、 ❌ でした。

openSSL 関連の Pythonライブラリや Django-SSL 関連の pip 、どれを使っても ローカル環境で SSL 通信は ❌ 。それであれば自分で VPS に SSL をセット、 とも考えましたが、 Stripe テストしたいだけなのに時間と手間がかかり過ぎるので、いろいろ考えた結果 PythonAnywhere が Stripe をテストするのに一番最適だと考えました。

【PythonAnywhere】
+ 無料
+ サブドメインで SSL 即・利用可能
+ サーバー設定不要で Flask や Django 使える
+ クラウド環境なので、やや遅い感じもする

「Stripe も Flask も PythonAnywhere も初めて」という方は少し時間かかるかもしれませんが、決済ボタンが動き出したら面白いので根気よく頑張ってみましょう。

Stripeの基本的な使い方

最終的には Webページ に決済ボタンをセットしますが、その前に Stripe の基本的な動きを確認しておきましょう。

image

まずは Stripe(https://stripe.com) にユーザー登録(無料プラン)し、上記の支払い画面を確認。新規でアカウントを作成したばかりであれば、支払いはナシになっているはずです。

"支払い" はちょっと誤訳かもしれません。自分の設定アイテムに対して 決済 が行われた "売り上げデータ" です。

ココのセクションでは、プログラムから Stripe の決済を実行し、その内容が Stripe の管理画面に反映されるかを確認してみます。

まずはログインした状態で、公式ドキュメントの Accept Payment(https://stripe.com/docs/payments/accept-a-payment) を確認。

いろいろ書かれていますが、 「1 Set up Stripe」 と 「2 Create a PaymentIntent支払内容」 を実行します。

上記コマンドは Mac、 Linux 向け。 Windows の方は専用コマンドで仮想環境を作成してください。

まずはローカル環境で仮想環境を作成し、 Stripe を pip install しておきます。これで 「1 Set up Stripe」 の作業が完了。

上記コードについて

import stripe

stripe.api_key = 'sk_test_LBL1Z97QvXa38NGd6FU0nEEo'

intent = stripe.PaymentIntent.create(
  amount=1099,
  currency='usd',
)

print(intent)

次は 「2 Create a PaymentIntent支払内容」 の実行で、 [公式ドキュメント](https://stripe.com/docs/payments/accept-a-payment) に書かれている上記コードを Python ファイルにコピペします。 この時 APIキーが表示されていますが、これは Stripe に ログイン してドキュメントを見ているためで、この APIキー は自分専用のもの。大事なので人に見せないように注意しましょう。

そしてプログラムを実行すると Stripe の PaymentIntent クラスの create() メソッドによって行われた内容が print(intent) で出力(上図・右側参照)。 JSON 形式のデータで、 1件の決済にたくさんの情報が含まれていることが確認できます。また API でこうした決済情報を操作することもできそうですね。

そして Stripe の管理画面(支払い)を見てみると、

$10.99 未完了 pi_1FtFcdDNDiIzN3myB8rpGbNB 2019/12/25 0:48

というデータが。これは今プログラムを実行した結果のデータで、説明のところをクリックすると詳細データを確認できます。このように数行のプログラムを実行するだけで Stripe 決済は実行できます。ただ、 "未完了" なんですよね。これは支払いの "認証" を行えていないため。通常 "決済" を実行しようと思うと、カードなどの情報入力も必要ですし、 Stripe の場合は "キーの認証" も必要。こうした Stripe 決済に必要な情報がないために "未完了" となっています。

上図右側のコードについて

import stripe
stripe.api_key = 'sk_test_LBL1Z97QvXa38NGd6FU0nEEo'

stripe.PaymentIntent.create(
  amount=70, 
  currency='jpy',
  payment_method_types=['card'],
  receipt_email='sample@gmail.com',
  description='〇〇商店のおすすめ品',
)

また stripe.PaymentIntent.create() 内で使用できる項目は多数あり、例えば通貨を円で、決済完了後は指定の宛先にメール送信、ということも可能(上図参照)です。こうした細かいデータ設定を行えるため、売上商品の管理やデータ分析も可能になります。

公式ドキュメントでは日本円の最小請求額は "50円" となっていますが、 "50円" でプログラムを実行すると「50セント以上の価格にセットしてください」とエラーが出ます。
つまり "円" で設定する場合は、為替変動も考慮すると "70円" ぐらいが最小請求単価になりますね。
70円に対して 3.6% 手数料が取られますので、実質手取りの最小単位は 67円、 その時の手数料は 3円です。

Stripeのカード入力画面を作る

次は公式ドキュメント* 「3 Collect card details」 の項目。 この項目、 クリアするのに一番難儀しました。公式に書かれているコードを見ても ???? ,,,,, だったからです。 皆さんは、上図の公式ドキュメントの HTML コードや JavaScript コードを見て、イメージがわきますか? またサンプルコードは GitHub でも紹介しているということですが、 GitHub のコードは最初のうちは見ない方がいいです。混乱します。

試行錯誤の上、とりあえず上図のように JavaScript や CSS を内包する 1つの HTML 雛形を用意することをお勧めします。そしてとりあえず HTMLファイルに公式ドキュメントの HTMLコードや JavaScript コードをコピペ。

上図のコードについて

<!DOCTYPE html>
<html>
<head>
    <title>Checkout</title>
    <script src="https://js.stripe.com/v3/"></script>
<style></style>
</head>
<body>
<div id="card-element">
  <!-- Elements will create input elements here -->
</div>
<!-- We'll put the error messages in this element -->
<div id="card-errors" role="alert"></div>
<button id="submit">Pay</button>

<script>
var stripe = Stripe('pk_test_6UDqKAb74YOfy4Jj3NhC02vR');
var elements = stripe.elements();

// Set up Stripe.js and Elements to use in checkout form
var style = {
  base: {
    color: "#32325d",
  }
};

var card = elements.create("card", { style: style });
card.mount("#card-element");

card.addEventListener('change', ({error}) => {
  const displayError = document.getElementById('card-errors');
  if (error) {
    displayError.textContent = error.message;
  } else {
    displayError.textContent = '';
  }
});

var submitButton = document.getElementById('submit');

submitButton.addEventListener('click', function(ev) {
  stripe.confirmCardPayment(clientSecret, {
    payment_method: {
      card: card,
      billing_details: {
        name: 'Jenny Rosen'
      }
    }
  }).then(function(result) {
    if (result.error) {
      // Show error to your customer (e.g., insufficient funds)
      console.log(result.error.message);
    } else {
      // The payment has been processed!
      if (result.paymentIntent.status === 'succeeded') {
        // Show a success message to your customer
        // There's a risk of the customer closing the window before callback
        // execution. Set up a webhook or plugin to listen for the
        // payment_intent.succeeded event that handles any business critical
        // post-payment actions.
      }
    }
  });
});
</script>
</body>
</html>

そして作成した HTML ファイルをブラウザで開いてみますと、自動的にカードの入力フォームが作成されていることが確認できます、ちょっと凄いですね。

試しにデモ用の VISAカード情報を入力してみると,,,, ❌ です。

サンプルカード番号: 4242 4242 4242 4242 を入力した結果。

理由は、 入力内容が Stripe の決済プロセス stripe.PaymentIntent.create() に渡されていないから。つまり

<form action="/charge" method="post" id="payment-form"> 

などで入力情報をバッググランド処理に渡す必要が。この処理、 Flask なら簡単に実行できそうですよね。

FlaskにStripeをセットする

今回は SSL対応の Flask を実行する必要がありますので、 PythonAnywhere(https://www.pythonanywhere.com/) で作業していきます。

はじめて PythonAnywhere を利用する場合は、必ずアカウントのメール認証プロセスを完了しておく必要があります。メール認証プロセスを完了しておかないと、 Stripe の決済実行時、エラーがでます。 私は最初のメール認証をおこなっていなかったせいで、この問題を解決するのに 3時間ぐらい消費しました。ご注意ください。

PythonAnywhereログイン後、 Flask App を起動し、 https に変更しておきます(上図参照)。そしてこれからこのクラウドコンピューター上で Stripe を実行するので、このコンピューターに Stripe を pip install しておく必要があります。

上図のように「Open Bash console here」 ボタンを押して、 コマンド画面に入ります。そして以下のコマンドを実行。

pip3 install --user Stripe

PythonAnywhere で pip instll する場合は、--user のオプションを付ける必要があります。

stripe のライブラリが用意できたら、 Flask の Python ファイルと HTML ファイルを編集します。

上記画像のPythonコード

import stripe
from flask import render_template, Flask, request

app = Flask(__name__)

STRIPE_SECRET_KEY="sk_test_LBL1Z97QvXa38NGd6FU0nEEo"

stripe.api_key = STRIPE_SECRET_KEY
@app.route('/')
def hello_world():
    return render_template('item.html')

@app.route('/charge', methods=['POST'])
def charge():
    user_email = request.form["email"]
    try:
        amount = 70
        customer = stripe.Customer.create(
            email=user_email,
            source=request.form['stripeToken']
        )
        stripe.Charge.create(
            customer=customer.id,
            amount=amount,
            currency='jpy',
            description='〇〇商店のおすすめ品(PythonAnywhere)',
            receipt_email=user_email,
        )
        return render_template('charge.html', amount=amount)
    except stripe.error.StripeError:
        return render_template('error.html')

まずはバックグランド側の Flask ファイルから上記のように編集します。トップディレクトリの URL にアクセスされたら item.html を返し、 item.html からカード情報が入力・投稿されたら def charge() を実行し、結果を charge.html に返す、という流れ。

Flask の基本的な操作に Stripe の決済プロセスを加えているだけ、のシンプルな処理内容です。

次はカード情報を入力する画面(item.html)や決済完了後の画面(charge.html)、エラー時の画面(error.html)を templates フォルダに用意(上図参照)。

各 HTML ファイルの様子

上記画面のHTMLコード

【item.html カード情報入力画面】
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
<style>
/**
 * The CSS shown here will not be introduced in the Quickstart guide, but shows
 * how you can use CSS to style your Element's container.
 */

input,
.StripeElement {
  box-sizing: border-box;

  height: 40px;

  padding: 10px 12px;

  border: 1px solid transparent;
  border-radius: 4px;
  background-color: white;

  box-shadow: 0 1px 3px 0 #e6ebf1;
  -webkit-transition: box-shadow 150ms ease;
  transition: box-shadow 150ms ease;
}

input,
.StripeElement--focus {
  box-shadow: 0 1px 3px 0 #cfd7df;
}

.StripeElement--invalid {
  border-color: #fa755a;
}

.StripeElement--webkit-autofill {
  background-color: #fefde5 !important;
}
</style>

</head>
<body>
  <script src="https://js.stripe.com/v3/"></script>

  <h1>おすすめ商品</h1>
  <hr>
  <h2>〇〇</h2>
  <h2>70円</h2>
  <hr>

  <form action="/charge" method="post" id="payment-form">
    <div class="form-row inline">
        <div class="col">
            <label for="card-element">
            Credit or debit card
            </label>
            <div id="card-element">
            <!-- A Stripe Element will be inserted here. -->
            </div>
        </div>
        <div class="col">
        <label for="email">
        Email Address
        </label><br>
        <input id="email" name="email" type="email" placeholder="〇〇○@example.com"  size="50" required>
        </div>
        </div>
      <!-- Used to display form errors. -->
      <div id="card-errors" role="alert"></div>
    </div>

    <button>Submit Payment</button>
  </form>

<script>
// Create a Stripe client.
var stripe = Stripe('pk_test_6UDqKAb74YOfy4Jj3NhC02vR');

// Create an instance of Elements.
var elements = stripe.elements();

// Custom styling can be passed to options when creating an Element.
// (Note that this demo uses a wider set of styles than the guide below.)
var style = {
  base: {
    color: '#32325d',
    fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
    fontSmoothing: 'antialiased',
    fontSize: '16px',
    '::placeholder': {
      color: '#aab7c4'
    }
  },
  invalid: {
    color: '#fa755a',
    iconColor: '#fa755a'
  }
};

// Create an instance of the card Element.
var card = elements.create('card', {style: style});

// Add an instance of the card Element into the `card-element` <div>.
card.mount('#card-element');

// Handle real-time validation errors from the card Element.
card.addEventListener('change', function(event) {
  var displayError = document.getElementById('card-errors');
  if (event.error) {
    displayError.textContent = event.error.message;
  } else {
    displayError.textContent = '';
  }
});

// Handle form submission.
var form = document.getElementById('payment-form');
form.addEventListener('submit', function(event) {
  event.preventDefault();

  stripe.createToken(card).then(function(result) {
    if (result.error) {
      // Inform the user if there was an error.
      var errorElement = document.getElementById('card-errors');
      errorElement.textContent = result.error.message;
    } else {
      // Send the token to your server.
      stripeTokenHandler(result.token);
    }
  });
});

// Submit the form with the token ID.
function stripeTokenHandler(token) {
  // Insert the token ID into the form so it gets submitted to the server
  var form = document.getElementById('payment-form');
  var hiddenInput = document.createElement('input');
  hiddenInput.setAttribute('type', 'hidden');
  hiddenInput.setAttribute('name', 'stripeToken');
  hiddenInput.setAttribute('value', token.id);
  form.appendChild(hiddenInput);

  // Submit the form
  form.submit();
}
</script>
</body>
</html>

【charge.html 決済完了後の画面】
<h2>お買い上げありがとうございます</h2>
<h3>お買い上げ商品:〇〇</h3>
<h3>お支払金額: 70円</h3>
<hr>
<a href="https://oshimamasara9.pythonanywhere.com/">トップに戻る</a>

【error.html エラー発生時の画面】
error....

カード情報の入力を受け付ける item.html には、 <form action="/charge" method="post" id="payment-form"> ; を追加し、入力内容を /charge にポスト。ポストされた内容は、 バックグランド処理の Flaskファイルによって決済処理される予定です。

一旦ファイルが整ったら、 PythonAnywhere のサーバーを ReLoad し、自分のアプリの URL にアクセス。すると...

デザインは質素ですが、一応決済画面が登場しました。試しにデモ用のカード情報を入力し、決済してみると...

決済完了できました、 Stripe の管理画面を見ても今度は 【成功】 となっています。また決済内容を見てみますと...

カード情報と合わせて入力してもらったメールアドレスも登録されて、商品概要の説明書きも記録されます。あとは APIキーを本番用に置き換えれば実課金を実行でき、 Web上でビジネス展開が可能に。


支払い完了後のメール設定を行っていますが、 "デモ版" ではメール送信されません。 また決済画面のデザインについては、いくつかテンプレートが用意されていますのでこちらを活用すると便利そうです。

https://stripe.dev/elements-examples


AIエンジニアに必要なスキルが身に付く

無料カウンセリングはこちら

まとめ

Stripe、参考テキストがあれば決して難しい内容ではないですが、公式ドキュメントを元に手探りで実行していくのは案外難しいものです。

今回のように "世間にはまだ浸透していないけれど使ってみたい機能" があった時、 HTML や JavaScript、 Python の基礎的な技術があればなんとか実行できるかもしれません。 少なくとも今回、 Stripe現行バージョンの <script src="https://js.stripe.com/v3/"></script> に対応したチュートリアル記事&動画はなく苦戦したものの、なんとかカード決済に成功しました。

ただ、基礎的な技術があってもアイディアが伴わなければそのサービスは完成できませんし、 アイディアだけで技術がなければもちろん何も始まりません。

これを読んでいる方の中には「楽して定期的にお金はいる方法ないかな...」「ブログもオワコンって言われてるしな...」「有料コンテンツを配信できるサイトって手数料どれぐらいなんだろう....」 とアイディア出しにかたよっていて、そこから先に進めてない方もいらっしゃると思います。

せっかくのアイディアを活かすために、アイディアを形にするために、プログラミング技術を手にしてみませんか? 自分の中でアイディアも技術もブートアップできれば、個人での Web 展開も楽しくなりそうですよね。

独学ではつまずいて先に進めなくなってしまうこともありますが、CodeCampのマンツーマンレッスンならそんな心配がありません。 200名以上の中から相性のいい講師を選べますので、

本当に "技術" を今習う価値があるかどうか、 無料体験レッスン で評価してみませんか? 試した結果、「これなら自分をブートアップできる、お金を払う価値がある」 と思って頂ければよしですし、 「だめだ、なんかわからないけど、コンピューターに抵抗ありすぎる...」 と感じられるようであれば他の自分ブートアップを再検索すればイイでしょう。

無料体験に関する詳しい内容は 公式ページ よりチェックしてみてください。

昨今のプログラミング・ニーズの高まりを受けて、無料体験枠が埋まっている日もあります。予め、ご了承ください。


オシママサラ
この記事を書いた人
オシママサラ
まずは7日間お試し!人気プログラミング講座を無料公開中
オンライン・プログラミングレッスンNo.1のCodeCamp