RailsでStripeを使って決済機能を実装する(仮払い機能付き)

サービスを開発していると決済機能をサービス内に実装したいことがよくあるかと思います。
今までちょくちょくstripeを利用してきたので、その経験を元に仮払い（オーソリ）込みの決済の実装方法を記述していこうと思います。

##Stripeとは
Stripeとはインターネットビジネス内に組み込むことができる決済サービスの１つです。Stripeを使えば、自分が開発しているサービスにクレジットカードを使った決済機能を組み込むことができます。決済方法が銀行振込のみのサービスにはぜひ導入を検討してみてください。

##stripeの手数料
stripeの手数料は決済毎に3.6%かかるのみです。導入費用や月ごとの利用料等は全て無料です。つまり、使った分だけ手数料がかかるという仕組みです。固定で発生する費用がないため、個人開発で売り上げがなかなか見込めない小さなサービスにもぴったりだと思います。

##Railsプロジェクトにstripeを導入する

それではstripeをRailsに導入してみましょう。
stripeはgemを用意してくれているので、導入はただgemをbundle installするだけです。

```ruby:Gemfile
  gem 'stripe'
```

stripeのgemを入れると、Rails内でstripeの様々なモデルを利用することができます。
単発の決済に利用するのは以下の２つのモデルです。

###Stripe::Customer
決済の際にまずstripeを利用する顧客情報を登録します。
emailやsourceなどのパラメータを渡します。

###Stripe::Charge
単発決済を行う際の決済情報を登録します。
決済金額(amount)や決済通貨(currency)などのパラメータを渡します。

###Stripe::Refund
決済のキャンセルを登録します。
仮払いの状態であれば全額返金可能。本決済完了後であればstripeの手数料(3.6%)が引かれた96.4%の金額が返金されます。

##Stripe Checkoutを実装する

それでは今回は[Airbnb](https://www.airbnb.jp/b/welcome-japan?af=43720035&c=.pi0.pk5950711093_23093472973_c_12026464216&sem_position=1t1&sem_target=kwd-12026464216&location_of_interest=&location_physical=1009537&ghost=true&gclid=Cj0KCQjwgezoBRDNARIsAGzEfe6oBKuXHaH7P9Nvx0T8Jy7lKPXk4NkcZ9IYtMWXxbOW3o5IpTFtJbgaAi32EALw_wcB)のような自分の部屋を貸すサービスを例にしてコードを書いていきたいと思います。
部屋の管理はRoomモデル、支払いの管理はPaymentモデルで行なっているとします。(1対1の関係)


まずはStripeのAPIキーをstripe.rbに入れておきます。

```ruby:stripe.rb

#開発環境とテスト環境にはstripeのテストモードのAPIキーを使用する
if Rails.env.development? || Rails.env.test?
  Rails.configuration.stripe = {
    :publishable_key => ENV['STRIPE_PUBLISHABLE_KEY'],
    :secret_key      => ENV['STRIPE_SECRET_KEY']
  }
end

#本番環境にはstripeの本番環境用APIキーを使用する
if Rails.env.production?
  Rails.configuration.stripe = {
    :publishable_key => ENV['STRIPE_PUBLISHABLE_KEY_PRODUCTON'],
    :secret_key      => ENV['STRIPE_SECRET_KEY_PRODUCTION']
  }
end

#それぞれの環境に適したstripeAPIキーをセットしておく。
Stripe.api_key = Rails.configuration.stripe[:secret_key]
```

次にroom_controllerというコントローラーにstripeを実装していきます。

```ruby:room_controller.rb

def create
    @room = Room.find(params[:room_id])

    begin

      ActiveRecord::Base.transaction  do
        @room.reserve = "仮予約"
        @room.save!
        #二重決済を防ぐためにすでにpaymentテーブルがユニークか調べる      
        raise "Already paid" if @room.payment.present? 
        #後でPaymentをnewする際に、万が一エラーが起こらないように、transactionの中でpayment_paramsの値を確認しておく
        raise "prameter error" unless params[:amount].present? && params[:stripeEmail].present? && params[:stripeToken].present?
      end

      ##############Stripe（決済）#########################################
        customer = Stripe::Customer.create(
          email: params[:stripeEmail],
          source: params[:stripeToken]
        )

        charge = Stripe::Charge.create(
          customer:       customer.id,
          amount:         params[:amount],
          description:    "「#{@room.name}」の決済",
          currency:       'jpy',
          receipt_email:  params[:stripeEmail],
          metadata: {'仮払い' => "1回目"},
          capture: false #capture:falseにすると仮払いで処理してくれる。
        )
      #####################################################################

      ###############決済記録を作成###################################################
        # stripeのcheckoutフォームから送られてきたパラメーターでpaymentのインスタンスを作成
        payment = Payment.new(payment_params)

        payment.email = customer.email # 支払った人がstripeのcheckoutフォームに入力したemail(支払い完了後、stripeからこのメールアドレスに支払い完了メールが送られる)
        payment.description = charge.description #決済の概要
        payment.currency = charge.currency  #通貨
        payment.customer_id = customer.id   # stripeのcustomerインスタンスのID
        payment.payment_date = Time.current # payment_date(支払いを行った時間)は現在時間を入れる
        payment.payment_status = "仮払い" # payment_status(この支払い)は仮払い状態(stripeのcaptureをfalseにしている)
        payment.uuid = SecureRandom.uuid  # 請求書の番号としてuuidを用意する
        payment.charge_id = charge.id  # 返金(refund)の時に使うchargeのIDをpaymentに保存しておく
        payment.stripe_commission = (charge.amount * 0.036).round  # stripeの手数料(3.6%)分の金額
        payment.amount_after_subtract_commission = charge.amount - payment.stripe_commission  # stripeの手数料(3.6%)分を引いた金額(依頼者が払った96.4%の金額)
        payment.receipt_url = charge.receipt_url  # この決済に対するstripeが用意してくれる領収書のURL
        payment.save!
      #############################################################################

      redirect_to temporary_complete_path #仮払い完了画面へ

    # stripe関連でエラーが起こった場合
    rescue Stripe::CardError => e
    flash[:error] = "#決済(stripe)でエラーが発生しました。{e.message}"
    render :new

    # Invalid parameters were supplied to Stripe's API
    rescue Stripe::InvalidRequestError => e
      flash.now[:error] = "決済(stripe)でエラーが発生しました（InvalidRequestError）#{e.message}"
      render :new

    # Authentication with Stripe's API failed(maybe you changed API keys recently)
    rescue Stripe::AuthenticationError => e
      flash.now[:error] = "決済(stripe)でエラーが発生しました（AuthenticationError）#{e.message}"
      render :new

    # Network communication with Stripe failed
    rescue Stripe::APIConnectionError => e
      flash.now[:error] = "決済(stripe)でエラーが発生しました（APIConnectionError）#{e.message}"
      render :new

    # Display a very generic error to the user, and maybe send yourself an email
    rescue Stripe::StripeError => e
      flash.now[:error] = "決済(stripe)でエラーが発生しました（StripeError）#{e.message}"
      render :new

    # stripe関連以外でエラーが起こった場合
    rescue => e
      flash.now[:error] = "エラーが発生しました#{e.message}"
      render :new
    end

  end
```
Paymentは領収書の役割を果たすクラスです。
gemのprownやwicked_pdfを使えば、簡単に領収書を作成できるかと思います。
ただ、stripeでは自動的に領収書が作成され、receipt_urlを使えば、作成された領収書のURLを引っ張ってくることもできます。

次にStripe Checkoutのフォームを作ります。

```erb:new.html.erb
<div>
    <%= form_tag room_path, id:"payForm" do %>

        <script src="https://checkout.stripe.com/checkout.js"></script>
        <%= hidden_field_tag 'stripeToken' %>
        <%= hidden_field_tag 'amount', @room.price %>
        <%= hidden_field_tag 'stripeEmail' %>
        <!-- 仮払いしたユーザーのID -->
        <%= hidden_field_tag 'user_id', current_user.id %>
        <!-- この仮払いのroomインスタンスを特定 -->
        <%= hidden_field_tag "room_id", @room.id %>

        <!-- 支払い済みであれば決済させない -->
        <% if @room.payment.present? %>
          <p class="btn btn-error">支払い済み</p>
        <% else %>
          <button id="btn-pay" type="button" class="btn btn-primary">仮払いする</button>
        <% end %>

        <script>
          // #ボタンを押した際のcheckoutのフォームはStripeCheckout.configureで設定する
            var handler = StripeCheckout.configure({
                //StripeのAPIキーを引っ張ってくる
                key: '<%= Rails.configuration.stripe[:publishable_key] %>',
                locale: 'auto', //言語の設定（autoの場合、ユーザのブラウザ規定言語が呼び出される）
                currency: 'jpy',
                // image: "image/directory", もしstripe checkoutのフォーム上部に画像を入れたい場合はここで指定する
                panelLabel: "{{amount}}のお支払い", //checkoutボタンの表示文字、{{amount}}の中に金額が入る
                allowRememberMe: false, //RememberMeを使いたい場合はここをtrueにする
                token: function(token,arg) { //ここでstripeTokenとstripeEmailに値を入れてsubmitする
                    document.getElementById('stripeToken').value = token.id;
                    document.getElementById('stripeEmail').value = token.email;
                    document.getElementById('payForm').submit();
                }
            });
            //Stripe Checkoutのフォームに表示される情報をここで指定する
            document.getElementById('btn-pay').addEventListener('click', function(e){
                handler.open({
                    name: '<%= @room.name %>',
                    description: '<%= @room.price %>円',
                    amount: document.getElementById("amount").value
                });
                e.preventDefault();
            })
        </script>
    <% end %>
</div>
```

これで仮払い機能の実装はできました。
あとはお部屋の貸し出しを大家さんがOKしてくれたら、本決済してあげるだけです。
つまり、capturedパラメータをtrueに変更してあげるだけです。(captureメソッドを使う)

```ruby:room_controller.rb
def pay_complete
  room = current_user.rooms.find(params[:room_id])

  begin

    ActiveRecord::Base.transaction  do

      room.reserve = "予約完了" #予約状況
      room.save!
      room.payment.payment_status = "完了" #支払い状況
      room.payment.save!

      #仮払いをcaputureして決済を完了させる
      charge_id = room.payment.charge_id
      charge = Stripe::Charge.retrieve(charge_id) #retrieveメソッドで仮払いしたchargeインスタンスを引っ張ってくることができる
      charge.capture #captureメソッドを使うと、本決済となり、実際にクレジットカードから決済が行われる。
    end

    redirect_to  complete_path #本決済完了画面へ

  #stripe関連でエラーが起こった場合
  rescue Stripe::CardError => e
  flash.now[:error] = "決済でエラーが発生しました。#{e.message}"
  render :new

  # Invalid parameters were supplied to Stripe's API
  rescue Stripe::InvalidRequestError => e
    flash.now[:error] = "決済(stripe)でエラーが発生しました（InvalidRequestError）#{e.message}"
    render :new

  # Authentication with Stripe's API failed(maybe you changed API keys recently)
  rescue Stripe::AuthenticationError => e
    flash.now[:error] = "決済(stripe)でエラーが発生しました（AuthenticationError）#{e.message}"
    render :new

  # Network communication with Stripe failed
  rescue Stripe::APIConnectionError => e
    flash.now[:error] = "決済(stripe)でエラーが発生しました（APIConnectionError）#{e.message}"
    render :new

  # Display a very generic error to the user, and maybe send yourself an email
  rescue Stripe::StripeError => e
    flash.now[:error] = "決済(stripe)でエラーが発生しました（StripeError）#{e.message}"
    render :new
  ########################################################################################
  # 下のレスキューはstripe関係以外でのエラーが起こった時に発動する
  rescue => e
    flash.now[:error] = "エラーが発生しました#{e.message}"
    render :new
  end
```

stripeの場合、仮払いの状態が７日間続いたものは自動的にキャンセルされるようになっています。
7日間以上仮払いの状態を保ちたい場合は、rakeタスクにて一旦Stripe::Refundクラスを使って既存の仮払いをキャンセルし、再度Stripe::Chargeから仮払いを行うと良いかと思います。

##さいごに
Stripeを使えば、自分のサービスに決済機能がつけられるので、サービス開発がさらに楽しくなりますね！