1# frozen_string_literal: true
2
3require_relative "./oob"
4
5class Registration
6 def self.for(iq, customer, web_register_manager)
7 EMPromise.resolve(customer&.registered?).then do |registered|
8 if registered
9 Registered.new(iq, registered.phone)
10 else
11 web_register_manager.choose_tel(iq).then do |(riq, tel)|
12 Activation.for(riq, customer, tel)
13 end
14 end
15 end
16 end
17
18 class Registered
19 def initialize(iq, tel)
20 @reply = iq.reply
21 @reply.status = :completed
22 @tel = tel
23 end
24
25 def write
26 @reply.note_type = :error
27 @reply.note_text = <<~NOTE
28 You are already registered with JMP number #{@tel}
29 NOTE
30 BLATHER << @reply
31 nil
32 end
33 end
34
35 class Activation
36 def self.for(iq, customer, tel)
37 if customer&.active?
38 Finish.new(iq, customer, tel)
39 elsif customer
40 EMPromise.resolve(new(iq, customer, tel))
41 else
42 # Create customer_id
43 raise "TODO"
44 end
45 end
46
47 def initialize(iq, customer, tel)
48 @reply = iq.reply
49 reply.allowed_actions = [:next]
50
51 @customer = customer
52 @tel = tel
53 end
54
55 attr_reader :reply, :customer, :tel
56
57 FORM_FIELDS = [
58 {
59 var: "activation_method",
60 type: "list-single",
61 label: "Activate using",
62 required: true,
63 options: [
64 {
65 value: "bitcoin",
66 label: "Bitcoin"
67 },
68 {
69 value: "credit_card",
70 label: "Credit Card ($#{CONFIG[:activation_amount]})"
71 },
72 {
73 value: "code",
74 label: "Referral or Activation Code"
75 }
76 ]
77 },
78 {
79 var: "plan_name",
80 type: "list-single",
81 label: "What currency should your account balance be in?",
82 required: true,
83 options: [
84 {
85 value: "cad_beta_unlimited-v20210223",
86 label: "Canadian Dollars"
87 },
88 {
89 value: "usd_beta_unlimited-v20210223",
90 label: "United States Dollars"
91 }
92 ]
93 }
94 ].freeze
95
96 def write
97 form = reply.form
98 form.type = :form
99 form.title = "Activate JMP"
100 form.instructions = "Going to activate #{tel} (TODO RATE CTR)"
101 form.fields = FORM_FIELDS
102
103 COMMAND_MANAGER.write(reply).then { |iq|
104 Payment.for(iq, customer, tel)
105 }.then(&:write)
106 end
107 end
108
109 module Payment
110 def self.kinds
111 @kinds ||= {}
112 end
113
114 def self.for(iq, customer, tel)
115 plan_name = iq.form.field("plan_name").value.to_s
116 customer = customer.with_plan(plan_name)
117 kinds.fetch(iq.form.field("activation_method")&.value&.to_s&.to_sym) {
118 raise "Invalid activation method"
119 }.call(iq, customer, tel)
120 end
121
122 class Bitcoin
123 Payment.kinds[:bitcoin] = method(:new)
124
125 def initialize(iq, customer, tel)
126 @reply = iq.reply
127 reply.note_type = :info
128 reply.status = :completed
129
130 @customer = customer
131 @customer_id = customer.customer_id
132 @tel = tel
133 @addr = ELECTRUM.createnewaddress
134 end
135
136 attr_reader :reply, :customer_id, :tel
137
138 def save
139 EMPromise.all([
140 REDIS.mset(
141 "pending_tel_for-#{customer_id}", tel,
142 "pending_plan_for-#{customer_id}", @customer.plan_name
143 ),
144 @addr.then do |addr|
145 REDIS.sadd("jmp_customer_btc_addresses-#{customer_id}", addr)
146 end
147 ])
148 end
149
150 def note_text(amount, addr)
151 <<~NOTE
152 Activate your account by sending at least #{'%.6f' % amount} BTC to
153 #{addr}
154
155 You will receive a notification when your payment is complete.
156 NOTE
157 end
158
159 def write
160 EMPromise.all([
161 @addr,
162 save,
163 BTC_SELL_PRICES.public_send(@customer.currency.to_s.downcase)
164 ]).then do |(addr, _, rate)|
165 min = CONFIG[:activation_amount] / rate
166 reply.note_text = note_text(min, addr)
167 BLATHER << reply
168 nil
169 end
170 end
171 end
172
173 class CreditCard
174 Payment.kinds[:credit_card] = ->(*args) { self.for(*args) }
175
176 def self.for(iq, customer, tel)
177 customer.payment_methods.then do |payment_methods|
178 if (method = payment_methods.default_payment_method)
179 Activate.new(iq, customer, method, tel)
180 else
181 new(iq, customer, tel)
182 end
183 end
184 end
185
186 def initialize(iq, customer, tel)
187 @customer = customer
188 @tel = tel
189
190 @reply = iq.reply
191 @reply.allowed_actions = [:next]
192 @reply.note_type = :info
193 @reply.note_text = "#{oob.desc}: #{oob.url}"
194 end
195
196 attr_reader :reply
197
198 def oob
199 oob = OOB.find_or_create(@reply.command)
200 oob.url = CONFIG[:credit_card_url].call(
201 @reply.to.stripped.to_s,
202 @customer.customer_id
203 )
204 oob.desc = "Add credit card, then return here and choose next"
205 oob
206 end
207
208 def write
209 COMMAND_MANAGER.write(@reply).then do |riq|
210 CreditCard.for(riq, @customer, @tel)
211 end
212 end
213
214 class Activate
215 def initialize(iq, customer, payment_method, tel)
216 @iq = iq
217 @customer = customer
218 @payment_method = payment_method
219 @tel = tel
220 end
221
222 def write
223 Transaction.sale(
224 @customer,
225 CONFIG[:activation_amount],
226 @payment_method
227 ).then(
228 method(:sold),
229 ->(_) { declined }
230 )
231 end
232
233 protected
234
235 def sold(tx)
236 tx.insert.then {
237 @customer.bill_plan
238 }.then do
239 Finish.new(@iq, @customer, @tel).write
240 end
241 end
242
243 DECLINE_MESSAGE =
244 "Your bank declined the transaction. " \
245 "Often this happens when a person's credit card " \
246 "is a US card that does not support international " \
247 "transactions, as JMP is not based in the USA, though " \
248 "we do support transactions in USD.\n\n" \
249 "If you were trying a prepaid card, you may wish to use "\
250 "Privacy.com instead, as they do support international " \
251 "transactions.\n\n " \
252 "You may add another card and then choose next"
253
254 def decline_oob(reply)
255 oob = OOB.find_or_create(reply.command)
256 oob.url = CONFIG[:credit_card_url].call(
257 reply.to.stripped.to_s,
258 @customer.customer_id
259 )
260 oob.desc = DECLINE_MESSAGE
261 oob
262 end
263
264 def declined
265 reply = @iq.reply
266 reply_oob = decline_oob(reply)
267 reply.allowed_actions = [:next]
268 reply.note_type = :error
269 reply.note_text = "#{reply_oob.desc}: #{reply_oob.url}"
270 COMMAND_MANAGER.write(reply).then do |riq|
271 CreditCard.for(riq, @customer, @tel)
272 end
273 end
274 end
275 end
276 end
277
278 class Finish
279 def initialize(iq, customer, tel)
280 @reply = iq.reply
281 @reply.status = :completed
282 @reply.note_type = :info
283 @reply.note_text = "Your JMP account has been activated as #{tel}"
284 @customer = customer
285 @tel = tel
286 end
287
288 def write
289 BandwidthTNOrder.create(@tel).then(&:poll).then(
290 ->(_) { @customer.register!(@tel).then { BLATHER << @reply } },
291 lambda do |_|
292 @reply.note_type = :error
293 @reply.note_text =
294 "The JMP number #{@tel} is no longer available, " \
295 "please visit https://jmp.chat and choose another."
296 BLATHER << @reply
297 end
298 )
299 end
300 end
301end