Chapter 06

アプリケーションの完成まで

arahabica
arahabica
2022.01.17に更新

ここからは駆け足で進めていきます。

6.1 環境変数の設定

3章で作成したLINEチャネルの下記の設定値を.envファイルに記載してください。

  • LINE_LOGIN_CHANNEL_ID
    • LINEログインチャネルのチャネルID
    • LINE Messaging APIのチャネルと混同しないよう注意!
  • LIFF_ID
    • LINEログインチャネルのLIFF ID
  • LINE_CHANNEL_SECRET
    • LINE Messaging APIチャネルのチャネルシークレット
  • LINE_CHANNEL_TOKEN
    • LINE Messaging APIチャネルのチャネルトークン
.env
LINE_LOGIN_CHANNEL_ID=YOUR_LINE_LOGIN_CHANNEL_ID
LIFF_ID=YOUR_LIFF_ID
LINE_CHANNEL_SECRET=YOUR_LINE_CHANNEL_SECRET
LINE_CHANNEL_TOKEN=YOUR_LINE_CHANNEL_TOKEN

6.2 スタンプの管理がユーザごとにされるように修正

app/controller/imprintes_controller.rb, app/controller/stamps_controller.rb

user_id = 1

とスタンプ(の押印)のuser_idを決め打ちしてしまっている箇所が4箇所あります。こちらを下記のようにログインユーザのIDになるよう書き換えてください。

user_id = current_user.id

6.3 LIFF IDを返すAPIを作成

今回はLIFF IDをサーバから与えてあげる構成にしています。
フロントの中に埋め込む構成にしても問題ありません。

コントローラを作成。

$ docker-compose exec web rails g controller config
$ docker-compose exec web chown -R "$(id -u $(whoami)):$(id -g $(whoami))" .

編集します。
環境変数LIFF_IDを返す単純なものです。認証前にアクセスしたいのでskip_before_actionで認証をスキップしています。

app/controllers/config_controller.rb
class ConfigController < ApplicationController
  skip_before_action :authenticate_user!

  def liff_id
    liffId = ENV['LIFF_ID']
    return render json: { liffId: liffId }, status: :ok
  end
end

config/routes.rbを修正。

config/routes.rb
Rails.application.routes.draw do
  root to: 'static#root'
  scope '/api' do
    scope format: 'json' do
      mount_devise_token_auth_for 'User', at: 'auth', controllers: {
        registrations: 'line_token_auth/registrations',
        sessions: 'line_token_auth/sessions'
      }
      resources :stamps, only: [:index, :show]
      resources :imprints, only: [:create]
      delete 'imprints', to: 'imprints#clear'
      get '/hello', to: 'hello#index'
+     get 'config/liff_id', to: 'config#liff_id'
    end
  end
end

6.4 クーポンを発行する処理

クーポンをLINEで送信するために、LINE Messaging APIのGemを追加します。
Gemfileに下記を追加します。

Gemfile
gem 'line-bot-api'

gemをインストールします。

$ docker-compose exec web bundle install

imprints_controller.rbの先頭でGemを読み込むように修正して、

app/controllers/imprints_controller.rb
require 'line/bot'

スタンプが7つ溜まった時にクーポンを発行する処理を入れます。

app/controllers/imprints_controller.rb
      if count >= 7
-       puts "send coupon"
+       if !send_coupon(current_user.uid)
+         return render json: { error: 'Error at sending message' }, status: :internal_server_error
+       end
      end

最後に、clientメソッドとsend_couponメソッドを下記のように定義します。

app/controllers/imprints_controller.rb
  def client
    @client ||= Line::Bot::Client.new { |config|
      config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
      config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
    }
  end
  def send_coupon(uid)
    message1 = {
      type: "text",
      text: "七福神コンプリートおめでとうございます🎉"
    }
    message2 = {
      type: "flex",
      altText: "クッキー引換券",
      contents: {
        type: "bubble",
        hero: {
          type: "image",
          url: "https://rsasage.s3.ap-northeast-1.amazonaws.com/connpass/event20220119/cookie.png",
          size: "full",
          aspectRatio: "10:10",
          aspectMode: "cover"
        },
        body: {
          type: "box",
          layout: "vertical",
          contents: [
            {
              type: "text",
              text: "クッキー引換券",
              weight: "bold",
              size: "xl"
            },
            {
              type: "text",
              text: "こちらの引換券を▢△◯堂のレジで見せてください",
              wrap: true
            }
          ]
        }
      }
    }
    response = client.push_message(uid, [message1, message2])
    response.code.to_i == 200
  end

6.5 フロントのログイン処理

次にフロントのログイン処理を修正していきます。
詳細は省きますが、フロントはvue3で書いています。

spa/src/components/Login.vue のスクリプトタグを下記のように修正してください。
LIFFのライブラリを読み込んで、APIでアクセストークンを送ってログインしているのが読み取れると思います。

spa/src/components/Login.vue
<script lang="ts">
import { defineComponent, onMounted } from 'vue'
import api from '@/api/api'
import liff from '@line/liff'
export default defineComponent({
  name: 'Login',
  setup: function (_, context) {
    onMounted(async () => {
      const liffId = await api.getLiffId()
      console.log({ liffId })
      await liff.init({ liffId })
      // LINEアプリ外で開いた時はログイン処理をする
      if (!liff.isLoggedIn()) {
        liff.login()
        return
      }
      const liffContext = liff.getContext()
      if (!liffContext || !liffContext.userId) {
        window.alert('コンテキストの取得に失敗しました。')
        return
      }
      const userId = liffContext.userId
      const accessToken = await liff.getAccessToken()
      if (!accessToken) {
        window.alert('LIFFの認証に失敗しました。')
        return
      }
      const res1 = await api.signIn(userId, accessToken)
      if (res1 && res1.status === 200) {
        context.emit('login')
        return
      }
      const res2 = await api.signUp(userId, accessToken)
      if (res2 && res2.status === 200) {
        context.emit('login')
        return
      }
      window.alert('認証に失敗しました。')
    })
  }
})
</script>

最後にspa/src/api/ApiClient.tsでトークンをヘッダに入れる処理を修正します。

spa/src/api/ApiClient.ts
  _getRequestHeaders (): AuthHeaders {
-   const headers = {
-     'access-token': '',
-     uid: '',
-     client: '',
-     expiry: ''
-   }
+   const headers = this.authStorage.get()
+   if (!headers) {
+     throw new Error('Not Authorized.')
+   }
    return { ...headers, 'Content-Type': 'application/json' }
  }

6.6 再ビルド & 再起動

docker-compose.ymlを元に戻します。

docker-compose.yml
-    #command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
-    command: "tail -f /dev/null"
+    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"

docker-compose uprails sしているターミナルをCtrl+Cします。
そして、コンテナを一度落とし、再ビルドし、起動します。

$ docker-compose down
$ docker-compose build
$ docker-compose up

作業はこれで終わりです。最後に動作確認していきましょう。

6.7 動作確認

LINEからLIFFアプリを開いて、スタンプを全て取得した後、下記のようにクーポンが送られてきたら成功です。
completed

お疲れ様でした🎉