registration.rb

  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