Rails6でStripe Checkoutを実装　（開発環境/TESTモード）

## 背景
Stripe決済を初めて実装する機会があり、参考記事があまりなく苦労したのでまとめてみました。

もし、Rails6でStripe決済を実装することになった駆け出しエンジニアがいたら是非参考にしてもらえると嬉しいです。
また、学習3~4ヶ月目のぺーぺーが書いた記事なので、理解が間違っている、リファクタリングなどあればなんなりと指摘していただけると幸いです。


Stripeには複数の決済手段があるが、今回は一番実装が簡単なCheckoutを実装しました。
ちなみにStripeの公式リファレンスではFWはSinatraなのでご注意ください。

<b>Checkoutとは？</b>: Stripeが用意してくれている決済画面に遷移して決済を行う手段である。


## 実装内容
- Stripe Checkout で決済機能を実装
    - 決済完了時にユーザー情報を登録
    - 支払完了後はWEBアプリに戻り、決済完了画面を表示
    - 決済せずにStripe上で「戻る」をしたときに支払キャンセル画面を表示


## Stripe側での設定

### 1. まずはStripeのサイトでアカウント作成

以下のリンクからアカウントを作成しましょう！
[Stripe公式サイト][0]
[0]:https://stripe.com/jp

### 2. ダッシュボード上の`商品`タブをクリックし、`＋ テスト商品を追加`をクリック
![ダッシュボード(商品).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1161057/66ec400f-a14f-337b-90ab-39eb39cca625.png)

### 3. 商品情報を入力
今回は一括払い、１，０００円の商品を作成
![商品作成ページ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1161057/702ebc27-d40c-7864-e36f-94a64a9d38b0.png)

### 4. 以下の４つのキーを使用

#### 商品ページ内
詳細のID: <b>prod_◯◯◯◯◯◯</b>
料金のAPI ID:<b>price_◯◯◯◯◯◯◯◯◯◯</b>
![商品IDと料金ID.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1161057/cdf72ddb-42ad-6121-acb0-64cdf8f7b1cd.png)

#### 開発者ページ内APIキーの標準キー
公開可能キー: pk_test_◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯…◯◯◯
シークレットキー: sk_test_◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯◯…◯◯◯　<font color="red">*第三者に漏洩しないようにご注意ください</font>
![公開可能キーとシークレットキー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1161057/6f42f4fb-3fef-45ed-9f6a-f2965c92f158.png)


## Rails側の手順

### <h3>■前提</h3>
deviseや必要なデータベースは作成済み。
今回、必要なデータベースは以下の感じ。
ユーザーテーブル： name, email, password, customer_id, plan_id + デフォルト
プランテーブル: id, plan_name + デフォルト

もしテーブルの作り方がわからない場合は、簡単にまとめてあるので以下を御覧ください！
参考:[マイグレーションファイルの基本][5]
[5]:https://qiita.com/vit_udon_husqy/items/6142908cca27b2188f0d

## 実装
少し長いので、コントローラは機能２つに分けて解説します。

### <h3>■Gemのインストール</h3>

```ruby:Gemfile
gem 'stripe' # Stripeを実装するためのgem
gem 'dotenv-rails' # Stripeの重要な情報を記述するためのgem
```
bundle installして準備完了

### <h3>■決済画面への遷移~決済完了ページ or 決済キャンセルページの実装</h3>
```ruby:payments_controller.rb
class PaymentsController < ApplicationController
  
　#決済後の画面遷移
  def new
    @session = Stripe::Checkout::Session.create({
      payment_method_types: ['card'],
      line_items: [{
        price: 'price_XXXXXXXXXXX',  # 料金API-IDを記述
        quantity: 1,
      }],
      mode: 'payment',
      success_url: request.base_url + '/payments/after_payment_register?session_id={CHECKOUT_SESSION_ID}',  # 決済成功後の遷移先
      cancel_url: request.base_url + '/payments/payment_cancel',  #決済キャンセルした際の遷移先
    })   
  end
  
  #決済前のキャンセルのアクション
  def payment_cancel
  end

end
```
Stripeでは、Railsに実装する際は`Stripe::Checkout::Session.◯◯◯`のような形でセッションオブジェクトを使用してデータを取得します。


参考:[Stripe API reference (Create)][1]
[1]:https://stripe.com/docs/api/checkout/sessions/create
参考:[Stripe Docs][7]
[7]:https://stripe.com/docs/checkout/integration-builder


```javascript:/payments/new.html.erb
<script src="https://js.stripe.com/v3"></script>

<script>
  var stripe = Stripe('pk_test_XXXXXXXXXXXXXX'); // ()内には公開可能キーを記述
  stripe.redirectToCheckout({
    sessionId: '<%= @session.id %>'
  }).then(function (result) {
  });
</script>
```
上記ではnewアクションに対するビューの設定です。つまり、決済画面を作っています。
newアクションで作ったインスタンス変数@session（料金、数量等の情報)を決済ページに持っていってくれます。
今回のケースだと `payments/new` というパスで決済ページにアクセスできます。
![スクリーンショット 2021-05-05 22.07.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1161057/8540bec5-e947-01b5-3186-8dbf12b183ee.png)
`← 戻る`の箇所には元々はショップ名が入ります。
カーソルを持っていくと`← 戻る`に変わり、クリックするとキャンセル扱いとなり画面遷移します。

ダミーカード番号 `4242 4242 4242 4242`と
その他の情報を適当に入力して「支払う」ボタンを押すと決済完了画面に遷移するはずです！


### <h3>■決済完了後、データベースにユーザー情報を登録</h3>

まず、Stripeの決済は色々な情報を保有しています。<b>欲しいデータが何なのか、事前に整理しておきましょう！</b>

どのようなデータが取れるのかは以下を参考にしてください。
参考:[Stripe API reference][2]
[2]:https://stripe.com/docs/api

今回、ユーザー情報としてStripeから引っ張りたかったのは以下の通り。
-顧客ID(customer_id)
-メールアドレス(email)
-商品ID(product_id)


```ruby:payments_controller.rb
class PaymentsController < ApplicationController
  
  
  before_action :user_plan_judgement, except: [:new, :payment_cancel]

  INITIAL_USER_NAME = "新規ユーザー"
  STRIPE_PRODUCT_ID = "prod_XXXXXX"  # 商品IDを記述
  
  #(中略)
  
  # 初期パスワードを乱数生成、初期ユーザー名を新規ユーザー、customer_id, email, plan_idをユーザーデータテーブルに登録
  def after_payment_register
    generated_initial_password = Devise.friendly_token.first(8)
    stripe_user_data = Stripe::Checkout::Session.retrieve(params[:session_id])    

    @user = User.new(
      customer_id: stripe_user_data.customer,
      name: INITIAL_USER_NAME,
      email: stripe_user_data.customer_details["email"],
      password: generated_initial_password,
      plan_id: user_plan_judgement
    )
    @user.save
    send_notification_email
  end

  private

  # 1.プランIDを取得
  def user_plan_judgement
    stripe_plan_data = Stripe::Checkout::Session.list_line_items(params[:session_id])
    if stripe_plan_data[:data][0][:price]["product"] == STRIPE_PRODUCT_ID
      plan_id = 1
    end
  end
end

```
ざっくりですが、以下のことを実行しています。
1. ユーザーが決済したプランはなにかを判定
2. 1.を含めたユーザー情報を登録

<b>1. ユーザーが決済したプランはなにかを判定</b>
`Stripe::Checkout::Session.list_line_items`でStripeの商品IDと引っ張ってきたデータのproductデータが一致していれば
plan_idに１を代入するという処理にしました。
現状あまり意味はないのですが、
<b>プランの料金が変わったとき</b>でも同一のプランであることとして管理するために設定しました。

参考:[Stripe API reference (line_items)][5]
[5]:https://stripe.com/docs/api/checkout/sessions/line_items

<b>2. 1.を含めたユーザー情報を登録</b>
`Devise.friendly_token.first(8)`でランダムな８桁のパスワードを設定
 
`Stripe::Checkout::Session.retrieve`でレトリーブしてきてくれます。
参考:[Stripe API reference (retrieve)][3]
[3]:https://stripe.com/docs/api/checkout/sessions/retrieve
かわいいレトリーバー犬はこのretrieveから来ているらしいです。
狩った獲物をとってくる（回収してくる)意味らしいです。　（今回のPJの恩師から教えてもらいました。)
参考:[語源由来辞典][4]
[4]:https://gogen-yurai.jp/retriever/
なので、欲しいデータをretrieve(回収)してきてもらうわけです。
retrieveしてもらった獲物(データ)を獲物箱(=変数)へ代入します。

あとは見慣れたUser.newとuser.saveでユーザーデータを保存してユーザー登録も完了！
実際に決済テストを行った後に`Rails console`の`User.all`コマンドで確認すると決済を行ったユーザーが登録されているはずです！

あとは決済後やキャンセル後のページをビューを整えて完成！
ビューで表示する内容はプロジェクトの指定のものか、テンプレート文を調べると良いと思います。

## <h3>完成形</h3>

### コントローラ
```ruby:payments_controller.rb
class PaymentsController < ApplicationController
  before_action :user_plan_judgement, except: [:new, :payment_cancel]

  INITIAL_USER_NAME = "新規ユーザー"
  STRIPE_PRODUCT_ID = "prod_XXXXXX"  # 商品IDを記述
  
  def new
    @session = Stripe::Checkout::Session.create({
      payment_method_types: ['card'],
      line_items: [{
        price: 'price_XXXXXXXXXXX',  # 料金API-IDを記述
        quantity: 1,
      }],
      mode: 'payment',
      success_url: request.base_url + '/payments/after_payment_register?session_id={CHECKOUT_SESSION_ID}',
      cancel_url: request.base_url + '/payments/payment_cancel',
    })   
  end

  def payment_cancel
  end

  def after_payment_register
    generated_initial_password = Devise.friendly_token.first(8)
    stripe_user_data = Stripe::Checkout::Session.retrieve(params[:session_id])    

    @user = User.new(
      customer_id: stripe_user_data.customer,
      name: INITIAL_USER_NAME,
      email: stripe_user_data.customer_details["email"],
      password: generated_initial_password,
      plan_id: user_plan_judgement
    )
    @user.save
    send_notification_email
  end

  private

  def user_plan_judgement
    stripe_plan_data = Stripe::Checkout::Session.list_line_items(params[:session_id])
    if stripe_plan_data[:data][0][:price]["product"] == STRIPE_PRODUCT_ID
      plan_id = 1
    end
  end
end

```

### ビュー
```javascript:/payments/new.html.erb
<script src="https://js.stripe.com/v3"></script>

<script>
  var stripe = Stripe('pk_test_XXXXXXXXXXXXXX'); // ()内には公開可能キーを記述
  stripe.redirectToCheckout({
    sessionId: '<%= @session.id %>'
  }).then(function (result) {
  });
</script>
```
```html:after_payment_register.html.erb
　<p>決済してユーザー登録したぞ</p>
  <p>決済後に表示されるページだよ</p>
  <p>必要に応じてhtmlで文章記述</p>
```
```html:payment_cancel.html.erb
  <p>キャンセルされたときに表示されるページだよ</p>
```

#### その他(ルーティング、env)
```ruby:routes.rb
(中略)

resources :payments, only: [:new]
  get '/payments/after_payment_register', controller: 'payments', action: 'after_payment_register'
  get '/payments/payment_cancel', controller: 'payments', action: 'payment_cancel'

(中略)

```
```:.env
*ここはGithubにpushされないファイルなので、隠したい定数はここに書きます。

//以下はシークレットキー
STRIPE_TEST_SECRET_KEY = sk_test_XXXXXXXXXXXXXXXXXX
```

## 終わりに
長くなりましたが、ご覧いただきありがとうございました！
今回、初めてStripe決済を実装する機会をいただくことができ、自分で実装した中で理解したことをまとめてみました。
`Stripe::Checkout::Session.◯◯◯`で自在にデータを取得できるのは本当に便利だなあと実感しました。
また、MVC、データの取得~保存、データベースの構築まで自分でやれたのでデータ関連の理解が深まったと感じます。

次回は本番環境の実装についてまとめられたらまとめてみようと思います。