#288 Billing with Stripe
- Download:
- source codeProject Files in Zip (101 KB)
- mp4Full Size H.264 Video (45.3 MB)
- m4vSmaller H.264 Video (21.1 MB)
- webmFull Size VP8 Video (22.9 MB)
- ogvFull Size Theora Video (52.7 MB)
Railsアプリケーションでクレジットカードによる支払い処理が必要な場合は、Stripeをぜひチェックしてみてください。Stripeは設定が簡単でとても開発者にやさしいペイメントゲートウェイ(カード決済サービス)です。使用料はトランザクションごとに発生するだけで、しかもとても良心的な価格設定です。月額の使用料やその他の隠れたコストもありません。(念のため、我々はこの記事でお金をもらっているわけではありません。)
Stripeは今のところ米国内でのみ利用可能で、自分のアプリケーションで利用したいという場合は米国の銀行に口座を持つ必要があります。米国以外への対応も現在進行中で、まもなく提供されると思われます。なおこれは海外の顧客に対して課金できないという意味ではなく、販売者側が米国内にいなければいけないという制限です。
RailsアプリケーションにStripeを追加する
今回のエピソードではStripeを使ってRailsアプリケーションに繰り返しの支払い処理を追加します。使用するアプリケーションはラマのキスを販売するサイトです。なぜラマか? 実はRyan Batesはラマ牧場で子供時代を過ごしたのですが、当時ラマのキスを売って一儲けしたいと思っていたのに機会に恵まれず、今になって過去の夢を取り戻そうとしているのです。
アプリケーションの大半はすでに出来上がっています。4つのプランがあり、ユーザはいずれかに申し込んで定期予約登録します。登録ページにはまだEメールアドレスのフィールドしかありませんが、フィールドに入力してフォームを送信すると、新しい定期予約が作成されます。Stripeを介して支払いを受けられるように、クレジットカードの情報を入力するためのフィールドを追加します。
Stripeの利用方法は簡単です。stripe.comのサイトで「Get Started with Stripe」ボタンをクリックすると 、テスト用トランザクションを処理できるページが表示されます。curlを使ってコマンドラインからも同じ操作が可能です。「Getting Started」のページからサンプルコードをコピーして、ターミナルウィンドウで実行すると、JSONが返されます。
$ curl https://api.stripe.com/v1/charges \ > -u zVyGPfdmfhSGpHAxhFiJT7kFnHeSi9ZN: \ > -d amount=100 \ > -d currency=usd \ > -d 'card[number]=4242424242424242' \ > -d 'card[exp_month]=5' \ > -d 'card[exp_year]=2012' \ > -d 'description=test charge IFnFXZgdZk' { "amount": 100, "created": 1318355241, "currency": "usd", "description": "test charge IFnFXZgdZk", "fee": 0, "id": "ch_oVfzHImv8sN5TV", "livemode": false, "object": "charge", "paid": true, "refunded": false, "card": { "country": "US", "exp_month": 5, "exp_year": 2012, "last4": "4242", "object": "card", "type": "Visa" } }
テスト用トランザクションをWebサイトからかまたはターミナルウィンドウから実行すると、Stripeの管理ページの下の方のダッシュボードに最近の支払いが表示されます。
アカウントの作成は簡単です。管理ページでEメールアドレスとパスワードを入力するだけです。登録ができたら、アカウントページでまず最初にStripeのREST APIと通信するためのAPIキーを確認できます。またStripeはRailsアプリケーションからこのAPIをとても簡単に利用できるようにするRuby gemを提供しています。アプリケーションにこのgemをインストールします。他のRuby gemと同じように、Gemfile
に参照情報を追加してbundle
コマンドを実行します。
source 'http://rubygems.org' gem 'rails', '3.1.1' gem 'sqlite3' # Gems used only for assets and not required # in production environments by default. group :assets do gem 'sass-rails', '~> 3.1.4' gem 'coffee-rails', '~> 3.1.1' gem 'uglifier', '>= 1.0.3' end gem 'jquery-rails' gem 'stripe'
Stripeは、操作対象のアカウントを識別するために2つのAPIキーを使用します。これらの情報を、アプリケーションの/config/initializers
ディレクトリに新しく作成したstripe.rb
という初期設定ファイルに保存します。ここで保存するキーは、アカウントページに表示されている秘密のテスト用キーと公開APIキーです。公開キーは、Ruby gemでは使われずビューとJavaScriptで使用するだけなので、定数に保存します。このキーにはStripeのアカウントページにある公開のテスト用キーの値を設定します。
Stripe.api_key = "5J7dr36JmNpLrXXFwAXChdRzZZwLyCHV" STRIPE_PUBLIC_KEY = "pk_B1SaM15nXXruDU3g2D6uns2kJeu9m"
このファイルには取り扱いに注意が必要な情報が含まれるので、アプリケーションの.gitignore
ファイルに追加して、リポジトリに含まれないようにするのがいいでしょう。また、本番環境のサイトでは別のキーを使うので、このファイルが誤って本番環境のコードにコピーされたら困ります。
StripeのJavaScriptとAPIキーを追加する
ユーザがクレジットカード情報をフォームに入力すると、その情報は直接Stripeのサーバに送信され、対象のアプリケーションを介することはありません。これによって、PCIコンプライアンスを得るのが非常に簡単になります。StripeのAPIページのサンプルフォームを見てみるとname
属性を持つフィールドは一つもありません。これはつまり、フォームが送信されてもこれらのフィールドは我々のアプリケーションのサーバには渡されないということです。その代わりに、ユーザがフォームを送信したときに実行されるJavaScriptコードに読み取られます。
アプリケーションでStripeを利用するときには、StripeのサーバにあるJavaScriptファイルをインクルードする必要があります。その上で公開キーを設定し、クレジットカード情報のフィールドを含むフォームが送信されたときに起動されるJavaScriptを追加します。このJavaScriptが、フォームのクレジットカード情報に基づいてトークンを生成します。
アプリケーションのレイアウトファイルにStripeのJavaScriptファイルを追加します。このファイルは、アプリケーション自身のJavaScriptファイルよりも前に置かれている必要があります。現在のページがチェックアウトページかどうかをチェックして、その場合のみJavaScriptを追加するコードを加えた方がいいのでしょうが、ここでは省略します。また公開キーをJavaScriptから利用できるようにする必要がありますが、これはRailsがCSRFキーをページのhead部分のmeta
タグに追加するのと同じ方法をとります。
<!DOCTYPE html> <html> <head> <title>Llama Kisses</title> <%= stylesheet_link_tag "application" %> <%= javascript_include_tag "https://js.stripe.com/v1/", "application" %> <%= csrf_meta_tags %> <%= tag :meta, :name => "stripe-key", :content => STRIPE_PUBLIC_KEY %> </head> <body> <!-- Body omitted --> </body> </html>
定期会員登録フォームにクレジットカード情報のフィールドを追加する
次に定期会員登録フォームにクレジットカード情報のフィールドを追加します。フォームには現在、選択されたプランのid
を保持する隠しフィールドとEメールアドレスのためのテキストフィールドがあります。これに、クレジットカード番号、セキュリティコード、期限日のためのフィールドを追加します。
<%= form_for @subscription do |f| %> <% if @subscription.errors.any? %> <div class="error_messages"> <h2><%= pluralize(@subscription.errors.count, "error") %> prohibited this subscription from being saved:</h2> <ul> <% @subscription.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <%= f.hidden_field :plan_id %> <div class="field"> <%= f.label :email %> <%= f.text_field :email %> </div> <div class="field"> <%= label_tag :card_number, "Credit Card Number " %> <%= text_field_tag :card_number, nil, name: nil %> </div> <div class="field"> <%= label_tag :card_code, "Security Code on Card (CVV)" %> <%= text_field_tag :card_code, nil, name: nil %> </div> <div class="field"> <%= label_tag :card_month, "Card Expiration" %> <%= select_month nil, {add_month_numbers_true: true}, {name: nil, id: "card_month"}%> <%= select_year nil, {start_year: Date.today.year, end_year: Date.today.year+15}, {name: nil, id: "card_year"}%> </div> <div class="actions"><%= f.submit "Subscribe" %></div> <% end %>
フォームに2つのテキストフィールド(クレジットカード番号とセキュリティコード用)と期限日用に2つのドロップダウンを追加しました。追加したフィールドについて、以下に少し補足します。クレジットカードの名前とセキュリティコードはSubscription
モデルの属性ではないので、フォームビルダーを通す代わりにlabel_tag
とtext_field_tag
を使用しました。それらの値はサーバに送らてほしくないので、合わせてそれぞれのフィールドの名前としてnil
を設定しました。同じように期限日のフィールドにはselect_month
とselect_year
を使用し、id
を手作業で設定して、Railsが生成したものを使わなくてもいいようにします。
ページをリロードすると、新しいフィールドがフォームに表示されます。
次にフォームが送信されたときに実行されるJavaScriptを書きます。このスクリプトはクレジットカード情報をStripeに送信し、フォームを介してStripeから受信したトークンを送信します。このアプリケーションはRails 3.1で作成されているため、subscriptions.js.coffee
ファイルのこのコードをCoffeeScriptで記述します。
jQuery -> Stripe.setPublishableKey($('meta[name="stripe-key"]').attr('content')) subscription.setupForm() subscription = setupForm: -> $('#new_subscription').submit -> $('input[type=submit]').attr('disabled', true) subscription.processCard() processCard: -> card = number: $('#card_number').val() cvc: $('#card_code').val() expMonth: $('#card_month').val() expYear: $('#card_year').val() Stripe.createToken(card, subscription.handleStripeResponse) handleStripeResponse: (status, response) -> if status == 200 alert(response.id) else alert(response.error.message)
このコードはページのDOMが読み込まれた後に実行されなくてはいけないので、全体をjQuery
関数でラップしています。このファイルはまず公開キーを指定してStripeを設定します。キーの値は、アプリケーションのレイアウトファイルに追加したmeta
タグから読み込まれます。値がstripe
であるname
属性を持ったmeta
タグを探し、そのcontent
属性を読み込んでそのキーの値を取得します。残りのロジックはsubscription
オブジェクトを介して処理されます。このオブジェクトが持つsetupForm
という関数がすべての処理を行います。
次にsubscription
オブジェクトを作成してsetupForm
関数を与えます。この関数では、id
から定期会員登録フォームを取得し、フォームのsubmit
イベントが起動したときに実行されるコールバックを追加します。フォームが送信されるときに誤って送信ボタンが再度押されることがないように、ボタンのdisabled属性をtrue
にして無効化します。その後でprocessCard
関数を呼び出し、false
を返してフォームが送信されないようにします。
processCard
関数が生成するオブジェクトが、クレジットカードの値をStripeが期待する形式で表し、val()
を用いてフォームのフィールドから必要な値を読みます。次の行は重要です。Stripe.createToken
を2つの引数と共に呼び出します。一つ目は今生成したcardオブジェクトです(オプション値を渡すこともできます)。2つ目はStripeからのレスポンスを非同期で処理する関数です。ここでsubscription.handleStripeResponse
を渡します。
handleStripeResponse
関数は2つの引数、status
とresponse
、をとります。もしstatusの値が200
だったらトランザクションは成功で、response.id
にレスポンス・トークンが保存されます。今はレスポンスの値をalert
表示して、その値を確認するだけにします。もしstatusが200
でなければエラーが発生したということなので、alert
でエラーメッセージを表示します。
ではここで新しいフォームを試してみます。新しい登録フォームをリロードして有効なカード情報を送信するとレスポンス・トークンが返信されます。
無効なカード番号を入力すると、今度はエラーメッセージが返されます。
レスポンスを処理する
Stripeへの呼び出しが正しく処理されることがわかったので、次はエラーメッセージを処理するコードを修正します。アラートを表示するのではなく、エラーメッセージをページ上のdivにstripe_errorのidと共に表示して、送信ボタンを再度有効化してユーザが情報を入力できるようにします。
handleStripeResponse: (status, response) -> if status == 200 alert(response.id) else $('#stripe_error').text(response.error.message) $('input[type=submit]').attr('disabled', false)
この修正後、ビューにdiv
を追加します。
<%= form_for @subscription do |f| %> <!-- Form fields omitted --> <div id="stripe_error"> <noscript>JavaScript is not enabled and is required for this form. First enable it in your web browser settings.</noscript> </div> <div class="actions"><%= f.submit "Subscribe" %></div> <% end %>
div
の中にnoscript
要素を置いて、JavaScriptが有効化されていないブラウザからフォームを利用しようとしたときにエラーが表示されるようにしました。ページをリロードして無効なカード番号を再度フォームから送信すると、alert
ではなくstripe_error
divの中にエラーメッセージが表示されます。
次に成功したトランザクションを処理するコードを修正します。レスポンス・トークンをalert
で表示するのではなく、フォームに新たに追加した隠しフィールドに入れて、フォームを送信します。ビューに新たに隠しフィールドを追加し、stripe_card_token
という名前にします。
<%= f.hidden_field :stripe_card_token %>
データベースのsubscriptions
テーブルにはstripe_card_token
フィールドがないので、このフィールドを仮の属性としてSubscription
モデルに追加します。
class Subscription < ActiveRecord::Base belongs_to :plan validates_presence_of :plan_id validates_presence_of :email attr_accessible :stripe_card_token end
CoffeeScriptファイルに戻って、alert
の部分を修正して、新たに作った隠しフィールドの値をレスポンス・トークンの値に設定しフォームを送信するコードに置き換えます。フォーム上で直接submit
を呼び出して、フォームを送信します。これによって、onsubmit
コールバックを迂回し、フォームのデータがPOSTでサーバに送信されます。
handleStripeResponse: (status, response) -> if status == 200 $('#subscription_stripe_card_token').val(response.id) $('#new_subscription')[0].submit() else $('#stripe_error').text(response.error.message) $('input[type=submit]').attr('disabled', false)
フォームはSubscriptionsController
のcreate
アクションに送られ、トークンが新しいSubscription
に渡されます。before_create
コールバックを介して支払処理をすることも可能ですが、そうではなくSubscription
モデルの中に新規にsave_with_payment
というメソッドを作成してそこで処理を行います。このメソッドを書く前に、save
の呼び出しをsave_with_payment
に置き換えます。
def create @subscription = Subscription.new(params[:subscription]) if @subscription.save_with_payment redirect_to @subscription, :notice => "Thank you for subscribing!" else render :new end end
save_with_payment
メソッドはstripe
gemを使って支払い処理を行います。レスポンス・トークンを使ってStripe::Charge.create
メソッドで支払いを行うこともできますが、レスポンス・トークンは1回しか使用できずここでは繰り返しの支払いを行いたいのでこの方法はとれません。その代わりにトークンを用いて新規のStripe::Customer
を作成します。そうすることで、そのユーザに対して定期的な支払いの予定を設定できます。これでStripeが繰り返しの支払いを自動処理してくれます。これらのすべてがStripeのすぐれたAPIドキュメントに記述されています。
Subscription
モデルの修正に取りかかる前に、Stripeアカウントにアクセスしていくつかのプランを設定して繰り返しの支払い処理の方法を事前登録しておきます。Railsアプリケーション中のプランに対応する名前とidを付けた4つのプランを追加します。
ここでSubscription
モデルに戻って、save_with_payment
メソッドを書きます。
class Subscription < ActiveRecord::Base belongs_to :plan validates_presence_of :plan_id validates_presence_of :email attr_accessor :stripe_card_token def save_with_payment if valid? customer = Stripe::Customer.create(description:email, plan: plan_id, card: stripe_card_token) self.stripe_customer_token = customer.id save! end end end
このメソッドはモデルが有効かチェックし、そうであれば新規にStripe::Customer
を作成し、description(顧客のEメールアドレス)、plan_id
、stripe_card_token
を渡します。Stripeが顧客のために新しいid
を作成するので、それをデータベースに保存します。新規にstripe_customer_token
フィールドに保存することにします。このフィールドのためのマイグレーションを作成し、rake db:migrate
を実行してデータベースに追加します。
$ rails g migration add_stripe_to_subscriptions stripe_customer_token:string
これで支払処理のテストを行う準備ができました。新しい登録ページを開いて、有効なカード情報を入力して「Subscribe」ボタンをクリックすると、バックグラウンドでトークンとcustomerが作成され、フォームが送信されます。Stripeアカウントの支払いページを見ると、そのプランの値段で新規の支払いが表示されています。これは繰り返しの支払いで毎月定期的に処理されます。
エラー処理
Stripe::Customer.create
の呼び出しは、情報の一部が不正な場合に例外を発生させる可能性があります。例えば、ユーザがJavaScriptを有効にせずにフォームを送信したためにstripe_card_token
が生成されなかった場合に発生します。このような場合への対応として、rescueを使用するのがいいでしょう。StripeがInvalidRequestError
を発生させるので、これを捕らえてログに記録し、ページに検証エラーを追加して再度フォームを表示します。
def save_with_payment if valid? customer = Stripe::Customer.create(description:email, ↵ plan: plan_id, card: stripe_card_token) self.stripe_customer_token = customer.id save! end rescue Stripe::InvalidRequestError => e logger.error "Stripe error while creating customer: #{e.message}" errors.add :base, "There was a problem with your credit card." end
また、クレジットカード情報に関係のないフォームフィールド(例えば今回のフォームのEメールフィールド)のエラーも、今よりもうまく処理する必要があります。現状では、Eメールアドレスなしで有効なクレジットカード情報で会員登録を実行した場合、Stripeから正規のトークンが届きますが、ログインが成功した旨を知らせるページにリダイレクトされる代わりに、メールアドレスの検証エラーと共に再度フォームが表示されてしまいます。
このような場合には、正規のトークンをすでに得たのでクレジットカード用フィールドは隠すべきです。そのためにはビューを修正して、定期予約のstripe_card_token
が存在する場合フィールドを隠して代わりにメッセージを表示します。
<div class="field"> <%= f.label :email %> <%= f.text_field :email %> </div> <% if @subscription.stripe_card_token %> Credit card has been provided <% else %> <div class="field"> <%= label_tag :card_number, "Credit Card Number " %> <%= text_field_tag :card_number, nil, name: nil %> </div> <div class="field"> <%= label_tag :card_code, "Security Code on Card (CVV)" %> <%= text_field_tag :card_code, nil, name: nil %> </div> <div class="field"> <%= label_tag :card_month, "Card Expiration" %> <%= select_month nil, {add_month_numbers_true: true}, ↵ {name: nil, id: "card_month"}%> <%= select_year nil, {start_year: Date.today.year, ↵ end_year: Date.today.year+15}, ↵ {name: nil, id: "card_year"} %> </div> </div> <% end %>
合わせてsubscriptionsのCoffeeScriptファイルのsetupForm
関数を修正して、このような場合にユーザがフォームを送信したときに、二重でカード処理が行われないようにします。これは、フォームにカード番号のフィールドが存在するかをチェックすることにより可能です。もし存在しなければ、それはクレジットカード番号がすでに処理されたので表示を隠されているということになります。この場合は再度処理はせず、true
を返してフォームが送信されるようにします。
setupForm: -> $('#new_subscription').submit -> $('input[type=submit]').attr('disabled', true) if $('#card_number').length subscription.processCard() false else true
ここで、有効なクレジットカード情報を入力し、Eメールアドレスは入力せずにフォームを送信すると、クレジットカードフィールドは隠されます。
Eメールフィールドを埋めてフォームを再送信すると定期予約の登録は成功し、Stripeによって支払い処理が行われます。
WebHook
今回触れることができなかったStripeの機能のひとつがWebHookです。この機能を使えば、特定のイベントが発生したときにStripeが呼び出すコールバックURLを利用できます。例えば繰り返しの支払いが失敗したときには、通知を受けてそのユーザのアカウントを保留できるようにしたいでしょう。そうすればユーザにカード情報を更新するように連絡することができます。カード情報の更新について言えば、作成するアプリケーションにはカード情報の更新やsubscriptionの解約などの手続きの手段があるべきですが、今回はその部分には触れていません。
Stripeには今回のエピソードでカバーできなかった機能がまた多くありますが、使い始めるために必要な情報は提供できたと思います。Stripeはクレジットカードによる支払い処理のためのすぐれた方法です。残念ながら米国以外ではまだ利用できませんが、米国以外への対応も近々行われる予定です。