# frozen_string_literal: true
require "forwardable"
require_relative "./backend_sgx"
require_relative "./ibr"
require_relative "./payment_methods"
require_relative "./plan"
class Customer
def self.for_jid(jid)
REDIS.get("jmp_customer_id-#{jid}").then do |customer_id|
raise "No customer id" unless customer_id
for_customer_id(customer_id)
end
end
def self.for_customer_id(customer_id)
result = DB.query_defer(<<~SQL, [customer_id])
SELECT COALESCE(balance,0) AS balance, plan_name, expires_at
FROM customer_plans LEFT JOIN balances USING (customer_id)
WHERE customer_id=$1 LIMIT 1
SQL
result.then do |rows|
new(customer_id, **rows.first&.transform_keys(&:to_sym) || {})
end
end
extend Forwardable
attr_reader :customer_id, :balance
def_delegator :@plan, :name, :plan_name
def_delegators :@plan, :currency, :merchant_account
def_delegators :@sgx, :register!, :registered?
def initialize(
customer_id,
plan_name: nil,
expires_at: Time.now,
balance: BigDecimal.new(0),
sgx: BackendSgx.new(customer_id)
)
@plan = plan_name && Plan.for(plan_name)
@expires_at = expires_at
@customer_id = customer_id
@balance = balance
@sgx = sgx
end
def with_plan(plan_name)
self.class.new(
@customer_id,
balance: @balance,
expires_at: @expires_at,
plan_name: plan_name
)
end
def bill_plan
EM.promise_fiber do
DB.transaction do
charge_for_plan
add_one_month_to_current_plan unless activate_plan_starting_now
end
end
end
def activate_plan_starting_now
DB.exec(<<~SQL, [@customer_id, plan_name]).cmd_tuples.positive?
INSERT INTO plan_log
(customer_id, plan_name, date_range)
VALUES ($1, $2, tsrange(LOCALTIMESTAMP, LOCALTIMESTAMP + '1 month'))
ON CONFLICT DO NOTHING
SQL
end
def payment_methods
@payment_methods ||=
BRAINTREE
.customer
.find(@customer_id)
.then(PaymentMethods.method(:for_braintree_customer))
end
def active?
@plan && @expires_at > Time.now
end
protected
def charge_for_plan
params = [
@customer_id,
"#{@customer_id}-bill-#{plan_name}-at-#{Time.now.to_i}",
-@plan.monthly_price
]
DB.exec(<<~SQL, params)
INSERT INTO transactions
(customer_id, transaction_id, created_at, amount)
VALUES ($1, $2, LOCALTIMESTAMP, $3)
SQL
end
def add_one_month_to_current_plan
DB.exec(<<~SQL, [@customer_id])
UPDATE plan_log SET date_range=range_merge(
date_range,
tsrange(
LOCALTIMESTAMP,
GREATEST(upper(date_range), LOCALTIMESTAMP) + '1 month'
)
)
WHERE
customer_id=$1 AND
date_range && tsrange(LOCALTIMESTAMP, LOCALTIMESTAMP + '1 month')
SQL
end
end