diff --git a/app/controllers/api/v1/callbacks_controller.rb b/app/controllers/api/v1/callbacks_controller.rb index d0d3484..943b4ef 100644 --- a/app/controllers/api/v1/callbacks_controller.rb +++ b/app/controllers/api/v1/callbacks_controller.rb @@ -71,6 +71,11 @@ def render_failure(error=nil) end def mobile_provider_params - params.require(:user).permit(:uid, :country_code, :mobile, :name, :id_token) + params.require(:user).permit(:uid, + :country_code, + :mobile, + :name, + :id_token, + :referral_code) end end diff --git a/app/controllers/api/v1/referrals_controller.rb b/app/controllers/api/v1/referrals_controller.rb new file mode 100644 index 0000000..87f4e06 --- /dev/null +++ b/app/controllers/api/v1/referrals_controller.rb @@ -0,0 +1,15 @@ +class Api::V1::ReferralsController < Api::BaseController + def create + if referral = Referral.create(referrer_id: current_user.id) + render json: referral, status: :created + else + render_error(:unprocessable_entity, referral.errors.full_messages.to_sentence) + end + end + + protected + + def referrals_params + params.require(:referrals).permit(:) + end +end diff --git a/app/controllers/api/v1/sessions_controller.rb b/app/controllers/api/v1/sessions_controller.rb index 70aede3..c7cab45 100644 --- a/app/controllers/api/v1/sessions_controller.rb +++ b/app/controllers/api/v1/sessions_controller.rb @@ -28,7 +28,12 @@ def create_params def create_session_for_user ensure_one_active_session - @session = Session.create!(create_params) + pending_referral = Referral.pending.for_candidate(current_user).first + create_params[:rewards] = pending_referral.reward if pending_referral + ActiveRecord::Base.transaction do + @session = Session.create!(create_params) + pending_referral.rewarded! + end end def ensure_one_active_session diff --git a/app/models/referral.rb b/app/models/referral.rb new file mode 100644 index 0000000..1e08328 --- /dev/null +++ b/app/models/referral.rb @@ -0,0 +1,24 @@ +class Referral < ApplicationRecord + CODE_PREFIX = 'STAYHOME'.freeze + REWARD_VALUE = 1000 + + enum status: { pending: 0, rewarded: 1 } + + scope :for_candidate, ->(user) { where(candidate_id: user.id) } + scope :for_referrer, ->(user) { where(referrer_id: user.id) } + + before_create :generate_code + + private + + def generate_code + new_code = "#{CODE_PREFIX}-#{SecureRandom.hex[0, 4].upcase}" + + if Referral.exists?(referrer_id: referrer_id, code: new_code) + generate_code + else + self.code = new_code + self.reward = REWARD_VALUE + end + end +end diff --git a/app/models/user.rb b/app/models/user.rb index dd1d018..f1f4201 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -20,6 +20,8 @@ class User < ApplicationRecord has_many :wallet_transactions, dependent: :destroy has_many :notification_tokens, dependent: :destroy has_many :sessions, dependent: :destroy + has_many :sent_referrals, class_name: 'Referral', foreign_key: :referrer_id + has_many :received_referrals, class_name: 'Referral', foreign_key: :candidate_id before_validation :remove_devise_validations, unless: :email_auth_validations after_validation :reverse_geocode @@ -48,10 +50,16 @@ def self.onboard_from_mobile(params) user = User.find_or_initialize_by(mobile: params[:mobile]) user.name = params[:name] user.identities.find_or_initialize_by(provider: 'mobile', uid: params[:uid]) + update_referrals(params[:referral_code]) if params[:referral_code] user.save_provider_auth_user user end + def update_referrals(code) + referral = Referral.find_by(code: code, candidate_id: nil) + referral.candidate = self if referral + end + def password_complexity return if password.blank? || password =~ /^(?=.*?[a-z])(?=.*?[#?!@$%^&*-]).{8,70}$/ errors.add :password, :password_complexity_error diff --git a/app/serializers/referral_serializer.rb b/app/serializers/referral_serializer.rb new file mode 100644 index 0000000..4e18014 --- /dev/null +++ b/app/serializers/referral_serializer.rb @@ -0,0 +1,3 @@ +class ReferralSerializer < ActiveModel::Serializer + attributes :code +end diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb index 3af6371..9329308 100644 --- a/app/serializers/user_serializer.rb +++ b/app/serializers/user_serializer.rb @@ -1,6 +1,6 @@ class UserSerializer < ActiveModel::Serializer attributes :id, :name, :email, :mobile, :profile_picture_url, :wallet_balance, - :home_duration_in_seconds, :lat, :lng + :home_duration_in_seconds, :lat, :lng, :total_referral_rewards def home_duration_in_seconds active_session = object.active_session @@ -20,4 +20,8 @@ def wallet_balance object.wallet_balance end end + + def total_referral_rewards + Referral.rewarded.for_referrer(object).sum(&:reward) + end end diff --git a/config/routes.rb b/config/routes.rb index cd45367..5d685d5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -27,6 +27,7 @@ resource :home, only: :index resource :user, only: :update do get :profile, to: 'users#show' + resources :referrals, only: :create end resources :notification_tokens, only: :create post '/sessions/ping', to: 'sessions#ping' diff --git a/db/migrate/20200513195719_create_referrals.rb b/db/migrate/20200513195719_create_referrals.rb new file mode 100644 index 0000000..560f280 --- /dev/null +++ b/db/migrate/20200513195719_create_referrals.rb @@ -0,0 +1,13 @@ +class CreateReferrals < ActiveRecord::Migration[5.2] + def change + create_table :referrals do |t| + t.references :referrer, index: true, foreign_key: { to_table: :users } + t.references :candidate, index: true, foreign_key: { to_table: :users }, null: true + t.integer :status, index: true, default: 0 + t.string :code, index: true + t.timestamp :expires_at + t.integer :reward + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 2d72474..7a6bc1a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2020_05_10_184356) do +ActiveRecord::Schema.define(version: 2020_05_13_195719) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -70,6 +70,20 @@ t.datetime "updated_at", null: false end + create_table "referrals", force: :cascade do |t| + t.bigint "referrer_id" + t.bigint "candidate_id" + t.integer "status", default: 0 + t.string "code" + t.datetime "expires_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["candidate_id"], name: "index_referrals_on_candidate_id" + t.index ["code"], name: "index_referrals_on_code" + t.index ["referrer_id"], name: "index_referrals_on_referrer_id" + t.index ["status"], name: "index_referrals_on_status" + end + create_table "questions", force: :cascade do |t| t.string "name" t.boolean "active", default: true, null: false @@ -148,6 +162,8 @@ t.index ["user_id"], name: "index_wallet_transactions_on_user_id" end + add_foreign_key "referrals", "users", column: "candidate_id" + add_foreign_key "referrals", "users", column: "referrer_id" add_foreign_key "answers", "questions" add_foreign_key "sessions", "users" end