RailsでStripe Connectを使ってみる


![connect.png](https://qiita-image-store.s3.amazonaws.com/0/116375/057e3ff7-0e06-3b39-9353-0d1893d8126b.png)

インターネットで商売をするのに、決済機能は欠かせません。Stripeは「決済API」で、今回取り扱う「Stripe Connect」はマーケットプレイスに特化した「決済フローAPI」です。

買い手と、売り手がいるマーケットプレイスを構築するためには、買い手によるクレジットカードの決済処理だけでなく、売り手への入金、またそのプラットフォーム（開発者側）による仲介手数料の回収も行なう必要があります。こういう状況に、Stripe Connectは最適です。

Stripe Connectの概念的な説明に関しましては、下記の記事をご参照ください。今回は、Railsで**0) Stripeアカウントを作成**、**1) Stripeのセットアップ**、**2) 売り手への入金処理**、**3) 買い手のクレジットカード登録**までを実装していく流れを紹介していきます。
https://qiita.com/y_toku/items/7bfa42793801dfc5415d

### 開発環境
Ruby 2.4.0
Rails 5.2.2
Devise
Stripe Standard

## 0) Stripeアカウントを作成する
Stripe Connectを開発環境で動かすためには、3つのAPIを取得する必要があります。
0-1) 公開可能キー（publishable_key）を取得します
0-2) シークレットキー（secret_key）を取得します
ダッシュボード > 開発者 > APIキーより取得できます。
<img width="1069" alt="api.png" src="https://qiita-image-store.s3.amazonaws.com/0/116375/0551265f-ff5c-4ac5-6181-d6cf44c8d581.png">

0-3) クライアントID（client_id)を取得します
ダッシュボード > Connect > 設定より取得できます。
<img width="1039" alt="connect_api.png" src="https://qiita-image-store.s3.amazonaws.com/0/116375/7940418d-9335-affe-93e1-8a58c5fe8290.png">

## 1) Stripeのセットアップ
まず、0)で取得してきたAPIをCredentials Keyに保存します。

```terminal:credentials.yml
stripe:
  publishable_key: pk_test_************
  secret_key: sk_test_************
  client_id: ca_************
```

必要なライブラリを`bundle install`でインストールしていきます。

```Ruby:Gemfile
gem 'stripe'
gem 'omniauth-stripe-connect'
```

## 2) 売り手への入金処理を実装する
`rails g migration AddStripeAccountToUsers access_code:string publishable_key:string`を実行して、UserモデルにStripeアカウントと紐づけるカラムを追加します。

```Ruby:2019xxxxx_add_stripe_account_to_users.rb
class AddStripeAccountToUsers < ActiveRecord::Migration[5.2]
  def change
    add_column :users, :access_code, :string
    add_column :users, :publishable_key, :string
  end
end
```

続いて、Stripeの設定とコールバックのコントローラーを追加していきます。

```Ruby:devise.rb
・・・
  config.omniauth :stripe_connect,
    Rails.application.credentials[Rails.env.to_sym][:stripe][:client_id],
    Rails.application.credentials[Rails.env.to_sym][:stripe][:secret_key],
    scode: 'read_write',
    stripe_landing: 'login'
・・・
```

```Ruby:omniauth_callbacks_controller.rb
・・・
def stripe_connect
  auth_data = request.env["omniauth.auth"]
  @user = current_user
  if @user.persisted?
    @user.access_code = auth_data.credentials.token
    @user.publishable_key = auth_data.info.stripe_publishable_key
    @user.save
    
    sign_in_and_redirect @user, event: :authentication
    flash[:notice] = 'Stripe Account Created And Connected' if is_navigational_format?
   else
     session["devise.stripe_connect_data"] = request.env["omniauth.auth"]
     redirect_to root_path
   end
end
・・・
```

ViewにStripe Connectボタンを追加します。

```Erb:edit.html.erb
・・・
   <%= link_to stripe_url do %>
     <%= image_tag ("connect-with-stripe.png") %>
   <% end %>
・・・
```

```application_helper.rb
module ApplicationHelper
  def stripe_url
   "https://connect.stripe.com/oauth/authorize?response_type=code&client_id=#{Rails.application.credentials[Rails.env.to_sym][:stripe][:client_id]}&scope=read_write"
  end    
end
```
Stripeボタンを押すと、下記のような画面に遷移します。
<img width="949" alt="skip.png" src="https://qiita-image-store.s3.amazonaws.com/0/116375/7d668ea7-ff6f-d251-756d-50f269951153.png">

開発モードでは「このアカウントフォームをスキップ」で入金口座の登録などをスキップできます。

最後に、StripeダッシュボードでリダイレクトURLを設定します。
<img width="1039" alt="connect_apiのコピー.png" src="https://qiita-image-store.s3.amazonaws.com/0/116375/735bd323-e69e-d35a-7e06-b3532ce4aa36.png">

access_codeと、publishable_keyが取得できていれば完了です。

## 3) 買い手のクレジットカード登録を実装する

クレジットカード情報を自身のアプリケーション上で持たず、フロントから直接Stripeに送るために、JSを使う必要があります。
今回は、RailsアプリケーションとJavaScriptの連携をスムーズに実現してくれる`gon`というgemを使います。

```Ruby:Gemfile
gem 'gon'
```

インストールしたgonをViewにセットアップします。

```Erb:application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>Stripe Application</title>
    <%= include_gon %>
    <%= Gon::Base.render_data %>
・・・
```

```Ruby:application_controller.rb
class ApplicationController < ActionController::Base
    before_action :assign_env_variable

    def after_sign_out_path_for(resource)
      new_user_session_path
    end

    def assign_env_variable
      gon.stripe_key = Rails.application.credentials[Rails.env.to_sym][:stripe][:publishable_key]
    end
・・・
```

続いて、Stripeのクレジットカード情報と紐づけるトークンを保存しておくカラムを追加します。

```Ruby:2019xxxxx_add_stripe_customer_id_to_users.rb
class AddStripeCustomerIdToUsers < ActiveRecord::Migration[5.2]
  def change
    add_column :users, :stripe_customer_id, :string
  end
end
```

config/initializers配下に、`stripe.rb`を設定します。

```Ruby:stripe.rb
Rails.configuration.stripe = {
  :publishable_key => Rails.application.credentials[Rails.env.to_sym][:stripe][:publishable_key],
  :secret_key      => Rails.application.credentials[Rails.env.to_sym][:stripe][:secret_key]
}

Stripe.api_key = Rails.configuration.stripe[:secret_key]
```

UserコントローラーとUserモデルに、下記を追加していきます。

```users_controller.rb
class UsersController < ApplicationController

  def update
    @user = User.find(params[:id])
    if @user.update(user_params)
      if @user.stripe_temporary_token.present?
        customer = Stripe::Customer.create(
          email: @user.email,
          source: @user.stripe_temporary_token
        )
        @user.update_attribute(:stripe_customer_id, customer.id)
      end
    else
      render :show
    end
    redirect_to @user
  end

  private

  def user_params
    params.require(:user).permit(:email, :stripe_customer_id, :stripe_temporary_token, :provider, :uid, :publishable_key, :access_code)
  end
end
```

```Ruby:user.rb
class User < ApplicationRecord
・・・
  attr_accessor :stripe_temporary_token
・・・
end
```

クレジットカードフォームをStripeに送るViewとJS部分を追加します。

```Erb:edit.html.erb
・・・
<%= form_for(@user, html: {class:'add-card'}) do |f| %>
  <div class="block">
    <%= f.hidden_field :stripe_temporary_token %>
    <div class="formItem">
      <%= f.label :card_number %>
      <div class="formInput"><%= text_field :card_number, placeholder: "Enter card number", data: {stripe: 'number'} %></div>
    </div>
    <div class="formItem">
      <%= f.label :expiration_month %>
      <div class="formInput"><%= select_month(Date.today, {add_month_numbers: true}, id: 'card_month', data: {stripe: 'exp-month'}) %></div>
    </div>
    <div class="formItem">
      <%= f.label :expiration_month %>
      <div class="formInput"><%= select_year(Date.today.year, {start_year: Date.today.year, end_year: Date.today.year + 10}, id: 'card_year', data: {stripe: 'exp-year'}) %></div>
    </div>            
    <div class="formItem">
      <%= f.label :security_code %>
      <div class="formInput"><%= text_field :card_cvc, data: {stripe: 'cvc'}, placeholder: 'CVV/CVC' %></div>
    </div>
  </div>
  <div class="block">
    <div class="button">
      <%= f.submit "Update", id: "submit-card", class: "btn btnSubmit" %>
    </div>
  </div>
<% end %>
・・・
```

```JavaScript:verify_card.js
jQuery(function($) {
  Stripe.setPublishableKey(gon.stripe_key);

  function createCardToken(event) {
  function createCardToken() {
    Stripe.card.createToken(
      {
        number: $("#card_number").val(),
        cvc: $("#card-cvc").val(),
        number: $("#card_number_").val(),
        cvc: $("#card_cvc_").val(),
        exp_month: $("#card_month").val(),
        exp_year: $("#card_year").val()
      },
      stripeResponseHandler
    );
    console.log("token created");
  }

  $("#submit-card").on("click", function(e) {
    createCardToken();
  });

  function stripeResponseHandler(status, response) {
    var $form = $(".add-card");

    if (response.error) {
      // Show the error on the form
      $("#error").text(response.error.message);
      $form.find("input[type=submit]").prop("disabled", false);
    } else {
      // response contains id and card, which contains additional card details
      var token = response.id;
      console.log(token);
      // Insert the token into the form so it gets submitted to the server
      $("input[id=stripe_temporary_token]").val(token);
      // and submit manually
      $form.get(0).submit();
    }
  }

  // If the user doesn't have a stripe customer account & credit card currently tied to their account, this function will execute and intercept the submit event to obtain a stripe token. Once it has it, it will execute the submit action
  $("#card-unavailable").each(function() {
    $(".add-card").submit(function(event) {
      console.log("no card");
      createCardToken();
      // Prevent the form from submitting with the default action, we want the token first.
      return false;
    });
  });
});
```

これで、stripe_customer_idが取れていれば完了です。

## さいごに

実装にあたって、Stripeにお問い合わせをしてみました。担当者の方が親身でかつ、迅速に対応してくれたため、開発の目処をすぐに立てることができました。

まだ日本ではStripe文献は少なく感じましたが、大変便利なのでぜひ使ってみましょう。

### 参考
[Stripe Connect & Rails — Part 1]("https://medium.com/@ThatGuyTinus/stripe-connect-rails-part-1-22ba7204725b")
[Stripe Connect & Rails — Part 2]("https://medium.com/@ThatGuyTinus/stripe-connect-rails-part-2-9751c0e553f9")