#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はクレジットカードによる支払い処理のためのすぐれた方法です。残念ながら米国以外ではまだ利用できませんが、米国以外への対応も近々行われる予定です。


