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