customer_plan.rb

 1# frozen_string_literal: true
 2
 3require "forwardable"
 4
 5class CustomerPlan
 6	extend Forwardable
 7
 8	attr_reader :expires_at
 9	def_delegator :@plan, :name, :plan_name
10	def_delegators :@plan, :currency, :merchant_account
11
12	def initialize(customer_id, plan: nil, expires_at: Time.now)
13		@customer_id = customer_id
14		@plan = plan || OpenStruct.new
15		@expires_at = expires_at
16	end
17
18	def active?
19		plan_name && @expires_at > Time.now
20	end
21
22	def bill_plan
23		EM.promise_fiber do
24			DB.transaction do
25				charge_for_plan
26				add_one_month_to_current_plan unless activate_plan_starting_now
27			end
28		end
29	end
30
31	def activate_plan_starting_now
32		DB.exec(<<~SQL, [@customer_id, plan_name]).cmd_tuples.positive?
33			INSERT INTO plan_log
34				(customer_id, plan_name, date_range)
35			VALUES ($1, $2, tsrange(LOCALTIMESTAMP, LOCALTIMESTAMP + '1 month'))
36			ON CONFLICT DO NOTHING
37		SQL
38	end
39
40protected
41
42	def charge_for_plan
43		params = [
44			@customer_id,
45			"#{@customer_id}-bill-#{plan_name}-at-#{Time.now.to_i}",
46			-@plan.monthly_price
47		]
48		DB.exec(<<~SQL, params)
49			INSERT INTO transactions
50				(customer_id, transaction_id, created_at, amount)
51			VALUES ($1, $2, LOCALTIMESTAMP, $3)
52		SQL
53	end
54
55	def add_one_month_to_current_plan
56		DB.exec(<<~SQL, [@customer_id])
57			UPDATE plan_log SET date_range=range_merge(
58				date_range,
59				tsrange(
60					LOCALTIMESTAMP,
61					GREATEST(upper(date_range), LOCALTIMESTAMP) + '1 month'
62				)
63			)
64			WHERE
65				customer_id=$1 AND
66				date_range && tsrange(LOCALTIMESTAMP, LOCALTIMESTAMP + '1 month')
67		SQL
68	end
69end