kaneko-joei / furima-31577

0 stars 0 forks source link

商品購入 1 #9

Closed kaneko-joei closed 3 years ago

kaneko-joei commented 3 years ago

What 商品購入機能

Why

n app/controllers/users_controller.rb:

+class UsersController < ApplicationController +

  • def show
  • Payjp.api_key = ENV["PAYJP_SECRET_KEY"]
  • card = Card.find_by(user_id: current_user.id)
  • redirect_to new_card_path and return unless card.present?
  • customer = Payjp::Customer.retrieve(card.customer_token)
  • @card = customer.cards.first
  • end
  • def update
  • if current_user.update(user_params)
  • redirect_to root_path
  • else
  • redirect_to action: "show"
  • end
  • end
  • private
  • def user_params
  • params.require(:user).permit(:name, :email)
  • end
  • +end こちらのコントローラーは本実装では不要ですので、削除しておきましょう!

削除しました

In config/routes.rb:

@@ -1,6 +1,19 @@ Rails.application.routes.draw do

  • get 'cards/new' get 'cards/new' こちらの記述は本実装要件に含まれないかと存じますので削除しておきましょう!

削除しました

In config/routes.rb:

root to: 'items#index'

  • resources :items
  • resources :items do
  • resources :purchases,only: [:index, :create]
  • resources :cards, only: [:new, :create] resources :cards, only: [:new, :create] こちらに関しても本実装要件に含まれないかと存じますので削除しておきましょう!

削除しました

In app/controllers/items_controller.rb:

def item_seller_confirmation

  • redirect_to root_path unless current_user.id == @item.user_id
  • redirect_to root_path unless current_user.id == @item.user_id || @item.purchase_management != nil redirect_to root_path unless current_user.id == @item.user_id || @item.purchase_management != nil こちらの条件式ですが、以下のように修正しましょう!

redirect_to root_path if current_user.id != @item.user_id || @item.purchase_management != nil 修正しました In app/models/purchase_address.rb:

@@ -0,0 +1,22 @@ +class PurchaseAddress

  • include ActiveModel::Model
  • attr_accessor :postal_code, :prefecture_id, :city, :addresses, :building_name, :phone_number, :user_id, :item_id, :token
  • with_options presence: true do
  • validates :postal_code, format: { with: /\A[0-9]{3}-[0-9]{4}\z/, message: "Include hyphen(-)" }
  • validates :city
  • validates :addresses
  • validates :phone_number, format: {with: /\A\d{11}\z/ , message: "is invalid."} validates :phone_number, format: {with: /\A\d{11}\z/ , message: "is invalid."} 現在ですと、11桁のみが保存できるようになっております。 実装要件には11桁以内とあるので、こちらを満たすように修正しましょう!

/\A\d{10,11}\z/ In spec/models/purchase_address_spec.rb:

+

  • context '購入できない場合' do
  • it "postal_codeがない場合は保存できないこと" do
  • @address.postal_code = ''
  • @address.valid?
  • expect(@address.errors.full_messages).to include("Postal code can't be blank")
  • end
  • it "postal_codeにハイフンがない場合は保存できないこと" do
  • @address.postal_code = '1112222'
  • @address.valid?
  • expect(@address.errors.full_messages).to include("Postal code Include hyphen(-)")
  • end
  • it "prefecture_idがない場合は保存できないこと" do
  • @address.prefecture_id = '1' @address.prefecture_id = '1' prefecture_idはinteger型なので''(シングルクォーテーション)は外しましょう!

外しました。

In app/javascript/card.js:

  • const form = document.getElementById("charge-form");
  • form.addEventListener("submit", (e) => { // イベント発火
  • e.preventDefault();
  • // カード情報の取得先
  • const formResult = document.getElementById("charge-form");
  • const formData = new FormData(formResult);
  • const card = { // カードオブジェクトを生成
  • number: formData.get("purchase_address[card_number]"), // カード番号
  • cvc: formData.get("purchase_address[card_cvc]"), // カード裏面の3桁の数字
  • exp_month: formData.get("purchase_address[card_exp_month]"), // 有効期限の月
  • exp_year: 20${formData.get("purchase_address[card_exp_year]")}, // 有効期限の年
  • };
  • Payjp.createToken(card, (status, response) => {
  • // console.log(status); // console.log(status); こちらのコメントアウトは、削除していただいて構いません。

削除しました

In app/javascript/card.js:

  • form.addEventListener("submit", (e) => { // イベント発火
  • e.preventDefault();
  • // カード情報の取得先
  • const formResult = document.getElementById("charge-form");
  • const formData = new FormData(formResult);
  • const card = { // カードオブジェクトを生成
  • number: formData.get("purchase_address[card_number]"), // カード番号
  • cvc: formData.get("purchase_address[card_cvc]"), // カード裏面の3桁の数字
  • exp_month: formData.get("purchase_address[card_exp_month]"), // 有効期限の月
  • exp_year: 20${formData.get("purchase_address[card_exp_year]")}, // 有効期限の年
  • };
  • Payjp.createToken(card, (status, response) => {
  • // console.log(status);
  • console.log("a") console.log("a") こちらのデバッグの記述は削除しましょう。

【理由】 開発時の挙動確認のための記述なので、実装後は必要ないためです。

削除しました。

In app/views/items/show.html.erb:

@@ -32,9 +32,9 @@ <%= link_to "削除", item_path, method: :delete, class:"item-destroy" %> <% else %> <%# 商品購入ボタン %>

  • <%# 商品が売れていない場合はこちらを表示しましょう %>
  • <%= link_to "購入画面に進む", item_path ,class:"item-red-btn"%>
  • <%# //商品が売れていない場合はこちらを表示しましょう %>
  • <%= link_to "購入画面に進む", item_purchases_path(@item.id) ,class:"item-red-btn"%>
  • <%# <% 'ユーザーがログインしている 且つ 商品が出品中である' %> <% if user_signed_in? %> <% if current_user.id == @item.user_id %> <%# 商品編集削除ボタン %> <%= link_to "商品の編集", edit_item_path, method: :get, class: "item-red-btn" %>

    or

    <%= link_to "削除", item_path, method: :delete, class:"item-destroy" %> <% else %> <%# 商品購入ボタン %>

    <%= link_to "購入画面に進む",  item_purchases_path(@item.id) ,class:"item-red-btn"%>

<% end %> <% end %> こちらの条件式では、商品が購入されている場合でも商品の編集と削除ボタンが表示されるかと存じます。

▼以下参考にしていただけると幸いです。

if [ユーザーはログインしているか?] かつ [商品は購入されていないか?]   if [現在ログインしているユーザーが商品の出品者であるか?]  商品の編集処理  商品の削除処理 else   購入画面に進む処理   end end if user_signed_in? && @item.purchase_management.nil? In spec/models/purchase_address_spec.rb:

@@ -0,0 +1,69 @@ +require 'rails_helper' + +RSpec.describe PurchaseAddress, type: :model do

  • before do
  • @item = FactoryBot.create(:item)
  • @user = FactoryBot.create(:user)
  • @address = FactoryBot.build(:purchase_address, item_id: @item.id, user_id: @user.id)
  • sleep(1)
  • end
  • context '購入できる場合' do
  • it "postal_code, prefecture_id, city, addresses, phone_number, user_id, item_id,がある場合は保存できること" do
  • expect(@address).to be_valid
  • end
  • end 正常系の以下のテストも実装しましょう。

建物の情報が無くても登録できる it "building_nameが無くても登録できる" do @address.building_name = '' expect(@address).to be_valid end In spec/models/purchase_address_spec.rb:

  • context '購入できる場合' do
  • it "postal_code, prefecture_id, city, addresses, phone_number, user_id, item_id,がある場合は保存できること" do
  • expect(@address).to be_valid
  • end
  • end
  • context '購入できない場合' do
  • it "postal_codeがない場合は保存できないこと" do
  • @address.postal_code = ''
  • @address.valid?
  • expect(@address.errors.full_messages).to include("Postal code can't be blank")
  • end
  • it "postal_codeにハイフンがない場合は保存できないこと" do
  • @address.postal_code = '-' @address.postal_code = '-' こちらは、ハイフンがない郵便番号などを代入して、テストしましょう!

1112222を代入

In spec/models/purchase_address_spec.rb:

  • it "prefecture_idがない場合は保存できないこと" do
  • @address.prefecture_id = ''
  • @address.valid?
  • expect(@address.errors.full_messages).to include("Prefecture can't be blank")
  • end it "prefecture_idがない場合は保存できないこと" do @address.prefecture_id = '' @address.valid? expect(@address.errors.full_messages).to include("Prefecture can't be blank") end プルダウン方式において、未選択の状態 と ' '(空) の状態は異なります。 モデル等をみながら、未選択の時にはどんな値が送られてくるのかを確認し、記述をしてみましょう!

プルダウン方式のところは数字1を入れました。

In spec/models/purchase_address_spec.rb:

  • @address.city = ''
  • @address.valid?
  • expect(@address.errors.full_messages).to include("City can't be blank")
  • end
  • it "Addressesがない場合は保存できないこと" do
  • @address.addresses = ''
  • @address.valid?
  • expect(@address.errors.full_messages).to include("Addresses can't be blank")
  • end
  • it "phone_numberがない場合は保存できないこと" do
  • @address.phone_number = ''
  • @address.valid?
  • expect(@address.errors.full_messages).to include("Phone number can't be blank")
  • end 更に堅牢なテストコードにするために、phone_numbeでは以下の異常系テストコードの実行お願いします。

12桁以上では登録できないこと 英数混合では登録できないこと it "12桁以上では登録できないこと" do @address.phone_number = '1112223334445' @address.valid? expect(@address.errors.full_messages).to include("Phone number is invalid.") end

it "英数混合では登録できないこと" do
  @address.phone_number = 'aaaaaaa111111'
  @address.valid?
  expect(@address.errors.full_messages).to include("Phone number is invalid.")
end 

In spec/models/purchase_address_spec.rb:

  • @address.valid?
  • expect(@address.errors.full_messages).to include("Phone number can't be blank")
  • end
  • it "user_idがない場合は保存できないこと" do
  • @address.user_id = ''
  • @address.valid?
  • expect(@address.errors.full_messages).to include("User can't be blank")
  • end
  • it "item_idがない場合は保存できないこと" do
  • @address.item_id = ''
  • @address.valid?
  • expect(@address.errors.full_messages).to include("Item can't be blank")
  • end
  • end 以下の異常系テストコードの追記お願いします。

tokenが空では登録できない。 https://gyazo.com/e092be217d995eedd9531415329e663a it "tokenが空では登録できない。" do @address.token = '' @address.valid? expect(@address.errors.full_messages).to include("Token can't be blank") end

n app/controllers/items_controller.rb:

  • def order

  • redirect_to new_card_path and return unless current_user.card.present?

  • Payjp.api_key = ENV["PAYJP_SECRET_KEY"] # 環境変数を読み込む

  • customer_token = current_user.card.customer_token # ログインしているユーザーの顧客トークンを定義

  • Payjp::Charge.create(

  • amount: @item.price, # 商品の値段

  • customer: customer_token, # 顧客のトークン

  • currency: 'jpy' # 通貨の種類(日本円)

  • )

  • ItemOrder.create(item_id: params[:id]) # 商品のid情報を「item_id」として保存する

  • redirect_to root_path

  • end

  • こちらのコメントアウトはItemsControllerには不要となります。 消しておきましょう!

削除しました。

In app/controllers/items_controller.rb:

@@ -3,6 +3,7 @@ class ItemsController < ApplicationController before_action :authenticate_user!, except: [:index, :show] before_action :set_item,only: [:show,:edit,:update,:destroy] before_action :item_seller_confirmation,only: [:edit,:update,:destroy]

  • before_action :find_item, only: :order before_action :find_item, only: :order こちらのメソッドは不要ですので、消しておきましょう!

削除しました。

In app/controllers/items_controller.rb:

@@ -52,13 +70,14 @@ def item_params params.require(:item).permit(:image,:name, :price, :description, :status_id, :shipping_cost_id, :shipping_day_id, :category_id, :prefecture_id).merge(user_id: current_user.id) end

In app/controllers/application_controller.rb:

@@ -1,6 +1,7 @@ class ApplicationController < ActionController::Base

  • before_action :authenticate_user! before_action :authenticate_user! こちらの記述はApplicationControllerに必要ございません。 消しておきましょう!

削除しました

理由といたしましては、本アプリでは各それぞれのControllerのアクションに対して適用させたいからです。 また、現在purchases_controller.rbにauthenticate_user!が定義されていないので、定義しておきましょう!

before_action :authenticate_user!定義しました。

In app/controllers/purchases_controller.rb:

  • redirect_to root_path unless @item.purchase_management == nil
  • redirect_to root_path if current_user == @item.user こちらの条件分岐は論理演算子を用いてまとめてしまいましょう! また、createアクションにも適用させたいので、beforeアクションを用いて実装しましょう!

def item_seller_confirmation redirect_to root_path unless current_user.id == @item.user_id || @item.purchase_management != nil

end In app/controllers/cards_controller.rb:

+class CardsController < ApplicationController

  • def new
  • end
  • def create
  • Payjp.api_key = ENV["PAYJP_SECRET_KEY"] # 環境変数を読み込む
  • customer = Payjp::Customer.create(
  • description: 'test', # テストカードであることを説明
  • card: params[:card_token] # 登録しようとしているカード情報
  • )
  • if card.save
  • redirect_to root_path
  • else
  • redirect_to action: "new" # カード登録画面
  • end
  • end
  • +end こちらのControllerは本実装では不要ですので、消しておきましょう!

削除しました。

In app/models/item_order.rb:

+class ItemOrder < ApplicationRecord +

  • belongs_to :item
  • +end こちらのモデルファイルは本アプリでは使用しないため、不要かと思います。 消しておきましょう!

削除しました。

In app/models/card.rb:

+class Card < ApplicationRecord +

  • belongs_to :user
  • +end こちらのモデルファイルは本アプリでは使用しないため、不要かと思います。 消しておきましょう!

削除しました—

Why

n app/controllers/items_controller.rb:

  • def find_item
  • @item = Item.find(params[:id])
  • end find_itemの中身が75行目と重複しているようですので、 本来実装したい内容を確認の上、修正いただくようお願いいたします。

削除しました。

In app/controllers/purchases_controller.rb:

@@ -0,0 +1,48 @@ +class PurchasesController < ApplicationController + +

  • def index
  • @item = (Item.find(params[:item_id])) index, createアクションに定義されている@item = Item.find(params[:item_id])は 同一ファイル内に記述が重複しているので、 private以下にメソッドを定義し、before_actionで呼び出しましょう

beforeアクションで呼び出し

privateでまとめました。

In app/controllers/purchases_controller.rb:

  • if @purchase_address.valid?
  • Payjp.api_key = ENV["PAYJP_SECRET_KEY"] # 環境変数を読み込む
  • Payjp::Charge.create(
  • amount: @item.price, # 商品の値段
  • card: address_params[:token], # カードトークン
  • currency: 'jpy' # 通貨の種類(日本円)
  • ) if @purchase_address.valid? Payjp.api_key = ENV["PAYJP_SECRET_KEY"] # 環境変数を読み込む
    Payjp::Charge.create(
    amount: @item.price,  # 商品の値段
    card: address_params[:token],    # カードトークン
    currency: 'jpy'                 # 通貨の種類(日本円)

    )

createアクションに冗長なコードを直接書くのではなく、 該当コードはをprivate以下にメソッドとして定義、 そのメソッドを呼び出してあげるようにしましょう。

privateへ移動しました。

In app/models/order.rb:

@@ -0,0 +1,3 @@ +class Order < ApplicationRecord

  • validates :price, presence: true validates :price, presence: true priceに関するバリデーションは、item.rbにて設定済みであるため不要となります。 削除しておきましょう!

削除しました。

In app/views/items/show.html.erb:

     <%# 商品が売れていない場合はこちらを表示しましょう %>
  • <%= link_to "購入画面に進む", item_path ,class:"item-red-btn"%>
  • <%= link_to "購入画面に進む", item_purchases_path(@item.id) ,class:"item-red-btn"%> <%# //商品が売れていない場合はこちらを表示しましょう %> 35,37行目のコメントアウトですが、 これらは実装案内用となり、実装後は不要な記述となります。

したがって、 実装が完了し、不要となれば削除しましょう。

削除しました

In app/views/purchases/index.html.erb:

     </h2>
-

¥<%= '999,999,999' %>

+

¥<%= @item.price %>

<%= '配送料負担' %>

<%= '配送料負担' %> 17行目の記述も正しく表示されるよう修正をお願いいたします。

@item shipping_costと記入しました。

In db/migrate/20210701095953_create_orders.rb:

@@ -0,0 +1,9 @@ +class CreateOrders < ActiveRecord::Migration[6.0] こちらのマイグレーションファイルは使用されておりますでしょうか?

商品の値段に関するカラムは、 itemテーブルのものを使用しておりますので 不要な場合はこちらのマイグレーションファイルを削除しておきましょう。

削除しました。

テスト成功画像 https://gyazo.com/fc6461ccb14599dfc3ddf766de667c2f https://gyazo.com/d4a50f3dc912a7671bf4ad3e32e6a42e

Why

必要な情報を適切に入力すると、商品の購入ができること https://gyazo.com/7e10a86486b17e130e8607c3700effbb 商品購入ページでは、一覧や詳細ページで選択した商品の情報が出力されること ログアウト状態のユーザーは、URLを直接入力して商品購入ページに遷移しようとすると、商品の販売状況に関わらずログインページに遷移すること https://gyazo.com/9682f949f3366c97a2c2483909edcb53 ログイン状態の出品者が、URLを直接入力して自身の出品した商品購入ページに遷移しようとすると、トップページに遷移すること https://gyazo.com/92b28fd886139e55d82b9f970e86bd08 ログイン状態のユーザーが、URLを直接入力して売却済み商品の商品購入ページへ遷移しようとすると、トップページに遷移すること https://gyazo.com/938021ba1da98aef8de2569a2bbf9b82

クレジットカード決済ができること クレジットカードの情報は購入の都度入力させること クレジットカード情報は必須であり、正しいクレジットカードの情報で無いときは決済できないこと 配送先の住所情報も購入の都度入力させること 配送先の情報として、郵便番号・都道府県・市区町村・番地・電話番号が必須であること 郵便番号にはハイフンが必要であること(123-4567となる) 電話番号にはハイフンは不要で、11桁以内であること(09012345678となる) 購入が完了したら、トップページまたは購入完了ページに遷移すること https://gyazo.com/7e10a86486b17e130e8607c3700effbb エラーハンドリングができていること(適切では無い値が入力された場合、情報は保存されず、エラーメッセージを出力させること) 入力に問題がある状態で購入ボタンが押されたら、購入ページに戻りエラーメッセージが表示されること

TakashiKozuma commented 3 years ago

レビュー依頼ありがとうございます! こちらのプルリクエストですが、実際の画面や機能ができているか確認させていただきたいので、以下のキャプチャ動画をご提出いただけますか?

- ログアウト状態のユーザーは、URLを直接入力して商品購入ページに遷移しようとすると、商品の販売状況に関わらずログインページに遷移すること(購入ページではなく、詳細ページに遷移しており、本来の挙動が確認できませんでした。)
- 入力に問題がある状態で購入ボタンが押されたら、購入ページに戻りエラーメッセージが表示されること
- テストの成功の画像

その後、再度レビュー依頼フォームにてプルリクエストURLのご提出いただけますでしょうか。 お手数おかけいたしますが、よろしくお願いします。

yutomentors commented 3 years ago

レビュー依頼ありがとうございます! こちらのプルリクエストですが、実際の画面や機能ができているか確認させていただきたいので、以下のキャプチャ動画をご提出いただけますか?

- テストの成功の画像

その後、再度レビュー依頼フォームにてプルリクエストURLのご提出いただけますでしょうか。 お手数おかけいたしますが、よろしくお願いします。