1# frozen_string_literal: true
2
3require "forwardable"
4
5require_relative "./blather_ext"
6require_relative "./customer_plan"
7require_relative "./customer_usage"
8require_relative "./backend_sgx"
9require_relative "./ibr"
10require_relative "./payment_methods"
11require_relative "./plan"
12require_relative "./sip_account"
13
14class Customer
15 def self.for_jid(jid)
16 REDIS.get("jmp_customer_id-#{jid}").then do |customer_id|
17 raise "No customer id" unless customer_id
18 for_customer_id(customer_id)
19 end
20 end
21
22 def self.for_customer_id(customer_id)
23 result = DB.query_defer(<<~SQL, [customer_id])
24 SELECT COALESCE(balance,0) AS balance, plan_name, expires_at
25 FROM customer_plans LEFT JOIN balances USING (customer_id)
26 WHERE customer_id=$1 LIMIT 1
27 SQL
28 result.then do |rows|
29 new(customer_id, **rows.first&.transform_keys(&:to_sym) || {})
30 end
31 end
32
33 def self.create(jid)
34 BRAINTREE.customer.create.then do |result|
35 raise "Braintree customer create failed" unless result.success?
36 cid = result.customer.id
37 REDIS.msetnx(
38 "jmp_customer_id-#{jid}", cid, "jmp_customer_jid-#{cid}", jid
39 ).then do |redis_result|
40 raise "Saving new customer to redis failed" unless redis_result == 1
41 new(cid)
42 end
43 end
44 end
45
46 extend Forwardable
47
48 attr_reader :customer_id, :balance
49 def_delegators :@plan, :active?, :activate_plan_starting_now, :bill_plan,
50 :currency, :merchant_account, :plan_name
51 def_delegators :@sgx, :register!, :registered?, :fwd_timeout=
52 def_delegators :@usage, :usage_report, :message_usage, :incr_message_usage
53
54 def initialize(
55 customer_id,
56 plan_name: nil,
57 expires_at: Time.now,
58 balance: BigDecimal.new(0),
59 sgx: BackendSgx.new(customer_id)
60 )
61 @plan = CustomerPlan.new(
62 customer_id,
63 plan: plan_name && Plan.for(plan_name), expires_at: expires_at
64 )
65 @usage = CustomerUsage.new(customer_id)
66 @customer_id = customer_id
67 @balance = balance
68 @sgx = sgx
69 end
70
71 def with_plan(plan_name)
72 self.class.new(
73 @customer_id,
74 balance: @balance,
75 expires_at: expires_at,
76 plan_name: plan_name
77 )
78 end
79
80 def payment_methods
81 BRAINTREE
82 .customer
83 .find(@customer_id)
84 .catch { OpenStruct.new(payment_methods: []) }
85 .then(PaymentMethods.method(:for_braintree_customer))
86 end
87
88 def jid
89 @jid ||= REDIS.get("jmp_customer_jid-#{customer_id}").then do |sjid|
90 Blather::JID.new(sjid)
91 end
92 end
93
94 def stanza_to(stanza)
95 jid.then do |jid|
96 stanza = stanza.dup
97 stanza.to = jid.with(resource: stanza.to&.resource)
98 stanza.from = stanza.from.with(domain: CONFIG[:component][:jid])
99 BLATHER << stanza
100 end
101 end
102
103 def stanza_from(stanza)
104 BLATHER << @sgx.stanza(stanza)
105 end
106
107 def sip_account
108 SipAccount.find(customer_id)
109 end
110
111 def reset_sip_account
112 SipAccount::New.new(username: customer_id).put.catch do
113 sip_account.then { |acct| acct.with_random_password.put }
114 end
115 end
116
117 def btc_addresses
118 REDIS.smembers("jmp_customer_btc_addresses-#{customer_id}")
119 end
120
121 def add_btc_address
122 REDIS.spopsadd([
123 "jmp_available_btc_addresses",
124 "jmp_customer_btc_addresses-#{customer_id}"
125 ])
126 end
127
128 protected def_delegator :@plan, :expires_at
129end