customer.rb

  1# frozen_string_literal: true
  2
  3require "forwardable"
  4
  5require_relative "./ibr"
  6require_relative "./payment_methods"
  7require_relative "./plan"
  8
  9class Customer
 10	def self.for_jid(jid)
 11		REDIS.get("jmp_customer_id-#{jid}").then do |customer_id|
 12			raise "No customer id" unless customer_id
 13			for_customer_id(customer_id)
 14		end
 15	end
 16
 17	def self.for_customer_id(customer_id)
 18		result = DB.query_defer(<<~SQL, [customer_id])
 19			SELECT COALESCE(balance,0) AS balance, plan_name, expires_at
 20			FROM customer_plans LEFT JOIN balances USING (customer_id)
 21			WHERE customer_id=$1 LIMIT 1
 22		SQL
 23		result.then do |rows|
 24			new(customer_id, **rows.first&.transform_keys(&:to_sym) || {})
 25		end
 26	end
 27
 28	extend Forwardable
 29
 30	attr_reader :customer_id, :balance
 31	def_delegator :@plan, :name, :plan_name
 32	def_delegators :@plan, :currency, :merchant_account
 33
 34	def initialize(
 35		customer_id,
 36		plan_name: nil,
 37		expires_at: Time.now,
 38		balance: BigDecimal.new(0)
 39	)
 40		@plan = plan_name && Plan.for(plan_name)
 41		@expires_at = expires_at
 42		@customer_id = customer_id
 43		@balance = balance
 44	end
 45
 46	def with_plan(plan_name)
 47		self.class.new(
 48			@customer_id,
 49			balance: @balance,
 50			expires_at: @expires_at,
 51			plan_name: plan_name
 52		)
 53	end
 54
 55	def bill_plan
 56		EM.promise_fiber do
 57			DB.transaction do
 58				charge_for_plan
 59				add_one_month_to_current_plan unless activate_plan_starting_now
 60			end
 61		end
 62	end
 63
 64	def payment_methods
 65		@payment_methods ||=
 66			BRAINTREE
 67			.customer
 68			.find(@customer_id)
 69			.then(PaymentMethods.method(:for_braintree_customer))
 70	end
 71
 72	def active?
 73		@plan && @expires_at > Time.now
 74	end
 75
 76	def register!(tel)
 77		BACKEND_SGX.register!(customer_id, tel)
 78	end
 79
 80	def registered?
 81		BACKEND_SGX.registered?(customer_id)
 82	end
 83
 84protected
 85
 86	def charge_for_plan
 87		params = [
 88			@customer_id,
 89			"#{@customer_id}-bill-#{plan_name}-at-#{Time.now.to_i}",
 90			-@plan.monthly_price
 91		]
 92		DB.exec(<<~SQL, params)
 93			INSERT INTO transactions
 94				(customer_id, transaction_id, created_at, amount)
 95			VALUES ($1, $2, LOCALTIMESTAMP, $3)
 96		SQL
 97	end
 98
 99	def activate_plan_starting_now
100		DB.exec(<<~SQL, [@customer_id, plan_name]).cmd_tuples.positive?
101			INSERT INTO plan_log
102				(customer_id, plan_name, date_range)
103			VALUES ($1, $2, tsrange(LOCALTIMESTAMP, LOCALTIMESTAMP + '1 month'))
104			ON CONFLICT DO NOTHING
105		SQL
106	end
107
108	def add_one_month_to_current_plan
109		DB.exec(<<~SQL, [@customer_id])
110			UPDATE plan_log SET date_range=range_merge(
111				date_range,
112				tsrange(
113					LOCALTIMESTAMP,
114					GREATEST(upper(date_range), LOCALTIMESTAMP) + '1 month'
115				)
116			)
117			WHERE
118				customer_id=$1 AND
119				date_range && tsrange(LOCALTIMESTAMP, LOCALTIMESTAMP + '1 month')
120		SQL
121	end
122end