registration.rb

  1# frozen_string_literal: true
  2
  3require "erb"
  4require "ruby-bandwidth-iris"
  5require "securerandom"
  6
  7require_relative "./alt_top_up_form"
  8require_relative "./bandwidth_tn_order"
  9require_relative "./bandwidth_tn_reservation_repo"
 10require_relative "./command"
 11require_relative "./em"
 12require_relative "./invites_repo"
 13require_relative "./oob"
 14require_relative "./parent_code_repo"
 15require_relative "./proxied_jid"
 16require_relative "./tel_selections"
 17require_relative "./welcome_message"
 18
 19class Registration
 20	def self.for(customer, google_play_userid, tel_selections)
 21		if (reg = customer.registered?)
 22			Registered.for(customer, reg.phone)
 23		else
 24			tel_selections[customer.jid].then(&:choose_tel).then do |tel|
 25				reserve_and_continue(tel_selections, customer, tel).then do
 26					FinishOrStartActivation.for(customer, google_play_userid, tel)
 27				end
 28			end
 29		end
 30	end
 31
 32	def self.reserve_and_continue(tel_selections, customer, tel)
 33		tel.reserve(customer).catch do
 34			tel_selections.delete(customer.jid).then {
 35				tel_selections[customer.jid]
 36			}.then { |choose|
 37				choose.choose_tel(
 38					error: "The JMP number #{tel} is no longer available."
 39				)
 40			}.then { |n_tel| reserve_and_continue(tel_selections, customer, n_tel) }
 41		end
 42	end
 43
 44	class Registered
 45		def self.for(customer, tel)
 46			jid = ProxiedJID.new(customer.jid).unproxied
 47			if jid.domain == CONFIG[:onboarding_domain]
 48				FinishOnboarding.for(customer, tel)
 49			else
 50				new(tel)
 51			end
 52		end
 53
 54		def initialize(tel)
 55			@tel = tel
 56		end
 57
 58		def write
 59			Command.finish("You are already registered with JMP number #{@tel}")
 60		end
 61	end
 62
 63	class FinishOrStartActivation
 64		def self.for(customer, google_play_userid, tel)
 65			if customer.active?
 66				Finish.new(customer, tel)
 67			elsif customer.balance >= CONFIG[:activation_amount_accept]
 68				BillPlan.new(customer, tel)
 69			else
 70				new(customer, google_play_userid, tel)
 71			end
 72		end
 73
 74		def initialize(customer, google_play_userid, tel)
 75			@customer = customer
 76			@tel = tel
 77			@google_play_userid = google_play_userid
 78		end
 79
 80		def write
 81			Command.reply { |reply|
 82				reply.allowed_actions = [:next]
 83				reply.note_type = :info
 84				reply.note_text = File.read("#{__dir__}/../fup.txt")
 85			}.then { Activation.for(@customer, @google_play_userid, @tel).write }
 86		end
 87	end
 88
 89	class Activation
 90		def self.for(customer, google_play_userid, tel)
 91			jid = ProxiedJID.new(customer.jid).unproxied
 92			if CONFIG[:approved_domains].key?(jid.domain.to_sym)
 93				Allow.for(customer, tel, jid)
 94			elsif google_play_userid
 95				GooglePlay.new(customer, google_play_userid, tel)
 96			else
 97				new(customer, tel)
 98			end
 99		end
100
101		def initialize(customer, tel)
102			@customer = customer
103			@tel = tel
104			@invites = InvitesRepo.new(DB, REDIS)
105		end
106
107		attr_reader :customer, :tel
108
109		def form
110			FormTemplate.render("registration/activate", tel: tel)
111		end
112
113		def write
114			Command.reply { |reply|
115				reply.allowed_actions = [:next]
116				reply.command << form
117			}.then(&method(:next_step))
118		end
119
120		def next_step(iq)
121			code = iq.form.field("code")&.value&.to_s
122			save_customer_plan(iq, code).then {
123				finish_if_valid_invite(code)
124			}.catch_only(InvitesRepo::Invalid) do
125				@invites.stash_code(customer.customer_id, code).then do
126					Payment.for(iq, @customer, @tel).then(&:write)
127				end
128			end
129		end
130
131	protected
132
133		def finish_if_valid_invite(code)
134			@invites.claim_code(@customer.customer_id, code) {
135				@customer.activate_plan_starting_now
136			}.then do
137				Finish.new(@customer, @tel).write
138			end
139		end
140
141		def save_customer_plan(iq, code)
142			ParentCodeRepo.new(redis: REDIS, db: DB).find(code).then do |parent|
143				plan = Plan.for_registration(iq.form.field("plan_name").value.to_s)
144				@customer = @customer.with_plan(plan.name, parent_customer_id: parent)
145				@customer.save_plan!
146			end
147		end
148
149		class GooglePlay
150			def initialize(customer, google_play_userid, tel)
151				@customer = customer
152				@google_play_userid = google_play_userid
153				@tel = tel
154				@invites = InvitesRepo.new(DB, REDIS)
155				@parent_code_repo = ParentCodeRepo.new(redis: REDIS, db: DB)
156			end
157
158			def used
159				REDIS.sismember("google_play_userids", @google_play_userid)
160			end
161
162			def form
163				FormTemplate.render(
164					"registration/google_play",
165					tel: @tel
166				)
167			end
168
169			def write
170				used.then do |u|
171					next Activation.for(@customer, nil, @tel).write if u.to_s == "1"
172
173					Command.reply { |reply|
174						reply.allowed_actions = [:next]
175						reply.command << form
176					}.then(&method(:activate)).then do
177						Finish.new(@customer, @tel).write
178					end
179				end
180			end
181
182			def activate(iq)
183				plan = Plan.for_registration(iq.form.field("plan_name").value)
184				code = iq.form.field("code")&.value
185				EMPromise.all([
186					@parent_code_repo.find(code),
187					REDIS.sadd("google_play_userids", @google_play_userid)
188				]).then { |(parent, _)|
189					save_active_plan(plan, parent)
190				}.then do
191					use_referral_code(code)
192				end
193			end
194
195		protected
196
197			def save_bogus_transaction
198				Transaction.new(
199					customer_id: @customer.customer_id,
200					transaction_id: "google_play_#{@customer.customer_id}",
201					amount: 0,
202					note: "Activated via Google Play",
203					bonus_eligible?: false
204				).insert
205			end
206
207			def save_active_plan(plan, parent)
208				@customer = @customer.with_plan(plan.name, parent_customer_id: parent)
209				save_bogus_transaction.then do
210					@customer.activate_plan_starting_now
211				end
212			end
213
214			def use_referral_code(code)
215				EMPromise.resolve(nil).then {
216					@invites.claim_code(@customer.customer_id, code) {
217						@customer.extend_plan
218					}
219				}.catch_only(InvitesRepo::Invalid) do
220					@invites.stash_code(@customer.customer_id, code)
221				end
222			end
223		end
224
225		class Allow < Activation
226			def self.for(customer, tel, jid)
227				credit_to = CONFIG[:approved_domains][jid.domain.to_sym]
228				new(customer, tel, credit_to)
229			end
230
231			def initialize(customer, tel, credit_to)
232				super(customer, tel)
233				@credit_to = credit_to
234			end
235
236			def form
237				FormTemplate.render(
238					"registration/allow",
239					tel: tel,
240					domain: customer.jid.domain
241				)
242			end
243
244			def next_step(iq)
245				plan = Plan.for_registration(iq.form.field("plan_name").value.to_s)
246				@customer = customer.with_plan(plan.name)
247				EMPromise.resolve(nil).then { activate }.then do
248					Finish.new(customer, tel).write
249				end
250			end
251
252		protected
253
254			def activate
255				DB.transaction do
256					if @credit_to
257						InvitesRepo.new(DB, REDIS).create_claimed_code(
258							@credit_to,
259							customer.customer_id
260						)
261					end
262					@customer.activate_plan_starting_now
263				end
264			end
265		end
266	end
267
268	module Payment
269		def self.kinds
270			@kinds ||= {}
271		end
272
273		def self.for(iq, customer, tel, final_message: nil, finish: Finish)
274			kinds.fetch(iq.form.field("activation_method")&.value&.to_s&.to_sym) {
275				raise "Invalid activation method"
276			}.call(customer, tel, final_message: final_message, finish: finish)
277		end
278
279		class CryptoPaymentMethod
280			def crypto_addrs
281				raise NotImplementedError, "Subclass must implement"
282			end
283
284			def reg_form_name
285				raise NotImplementedError, "Subclass must implement"
286			end
287
288			def sell_prices
289				raise NotImplementedError, "Subclass must implement"
290			end
291
292			def initialize(customer, tel, final_message: nil, **)
293				@customer = customer
294				@customer_id = customer.customer_id
295				@tel = tel
296				@final_message = final_message
297			end
298
299			def save
300				TEL_SELECTIONS.set(@customer.jid, @tel)
301			end
302
303			attr_reader :customer_id, :tel
304
305			def form(rate, addr)
306				amount = CONFIG[:activation_amount] / rate
307
308				FormTemplate.render(
309					reg_form_name,
310					amount: amount,
311					addr: addr,
312					final_message: @final_message
313				)
314			end
315
316			def write
317				EMPromise.all([addr_and_rate, save]).then do |((addr, rate), _)|
318					Command.reply { |reply|
319						reply.allowed_actions = [:prev]
320						reply.status = :canceled
321						reply.command << form(rate, addr)
322					}.then(&method(:handle_possible_prev))
323				end
324			end
325
326		protected
327
328			def handle_possible_prev(iq)
329				raise "Action not allowed" unless iq.prev?
330
331				Activation.for(@customer, nil, @tel).then(&:write)
332			end
333
334			def addr_and_rate
335				EMPromise.all([
336					crypto_addrs.then { |addrs|
337						addrs.first || add_crypto_addr
338					},
339
340					sell_prices.public_send(@customer.currency.to_s.downcase)
341				])
342			end
343		end
344
345		class Bitcoin < CryptoPaymentMethod
346			Payment.kinds[:bitcoin] = method(:new)
347
348			def reg_form_name
349				"registration/btc"
350			end
351
352			def sell_prices
353				BTC_SELL_PRICES
354			end
355
356			def crypto_addrs
357				@customer.btc_addresses
358			end
359
360			def add_crypto_addr
361				@customer.add_btc_address
362			end
363		end
364
365		## Like Bitcoin
366		class BCH < CryptoPaymentMethod
367			Payment.kinds[:bch] = method(:new)
368
369			def reg_form_name
370				"registration/bch"
371			end
372
373			def sell_prices
374				BCH_SELL_PRICES
375			end
376
377			def crypto_addrs
378				@customer.bch_addresses
379			end
380
381			def add_crypto_addr
382				@customer.add_bch_address
383			end
384		end
385
386		class CreditCard
387			Payment.kinds[:credit_card] = ->(*args, **kw) { self.for(*args, **kw) }
388
389			def self.for(in_customer, tel, finish: Finish, **)
390				reload_customer(in_customer).then do |(customer, payment_methods)|
391					if customer.balance >= CONFIG[:activation_amount_accept]
392						next BillPlan.new(customer, tel, finish: finish)
393					end
394
395					if (method = payment_methods.default_payment_method)
396						next Activate.new(customer, method, tel, finish: finish)
397					end
398
399					new(customer, tel, finish: finish)
400				end
401			end
402
403			def self.reload_customer(customer)
404				EMPromise.all([
405					Command.execution.customer_repo.find(customer.customer_id),
406					customer.payment_methods
407				])
408			end
409
410			def initialize(customer, tel, finish: Finish)
411				@customer = customer
412				@tel = tel
413				@finish = finish
414			end
415
416			def oob(reply)
417				oob = OOB.find_or_create(reply.command)
418				oob.url = CONFIG[:credit_card_url].call(
419					reply.to.stripped.to_s.gsub("\\", "%5C"),
420					@customer.customer_id
421				) + "&amount=#{CONFIG[:activation_amount]}"
422				oob.desc = "Add credit card, save, then next here to continue"
423				oob
424			end
425
426			def write
427				Command.reply { |reply|
428					reply.allowed_actions = [:next, :prev]
429					toob = oob(reply)
430					reply.note_type = :info
431					reply.note_text = "#{toob.desc}: #{toob.url}"
432				}.then do |iq|
433					next Activation.for(@customer, nil, @tel).then(&:write) if iq.prev?
434
435					CreditCard.for(@customer, @tel, finish: @finish).then(&:write)
436				end
437			end
438
439			class Activate
440				def initialize(customer, payment_method, tel, finish: Finish)
441					@customer = customer
442					@payment_method = payment_method
443					@tel = tel
444					@finish = finish
445				end
446
447				def write
448					CreditCardSale.create(
449						@customer,
450						amount: CONFIG[:activation_amount],
451						payment_method: @payment_method
452					).then(
453						->(_) { sold },
454						->(_) { declined }
455					)
456				end
457
458			protected
459
460				def sold
461					BillPlan.new(@customer, @tel, finish: @finish).write
462				end
463
464				DECLINE_MESSAGE =
465					"Your bank declined the transaction. " \
466					"Often this happens when a person's credit card " \
467					"is a US card that does not support international " \
468					"transactions, as JMP is not based in the USA, though " \
469					"we do support transactions in USD.\n\n" \
470					"You may add another card"
471
472				def decline_oob(reply)
473					oob = OOB.find_or_create(reply.command)
474					oob.url = CONFIG[:credit_card_url].call(
475						reply.to.stripped.to_s.gsub("\\", "%5C"),
476						@customer.customer_id
477					) + "&amount=#{CONFIG[:activation_amount]}"
478					oob.desc = DECLINE_MESSAGE
479					oob
480				end
481
482				def declined
483					Command.reply { |reply|
484						reply_oob = decline_oob(reply)
485						reply.allowed_actions = [:next]
486						reply.note_type = :error
487						reply.note_text = "#{reply_oob.desc}: #{reply_oob.url}"
488					}.then do
489						CreditCard.for(@customer, @tel, finish: @finish).then(&:write)
490					end
491				end
492			end
493		end
494
495		class InviteCode
496			Payment.kinds[:code] = ->(*args, **kw) { self.for(*args, **kw) }
497
498			def self.for(in_customer, tel, finish: Finish, **)
499				reload_customer(in_customer).then do |customer|
500					if customer.balance >= CONFIG[:activation_amount_accept]
501						next BillPlan.new(customer, tel, finish: finish)
502					end
503
504					msg = if customer.balance.positive?
505						"Account balance not enough to cover the activation"
506					end
507					new(customer, tel, error: msg, finish: Finish)
508				end
509			end
510
511			def self.reload_customer(customer)
512				Command.execution.customer_repo.find(customer.customer_id)
513			end
514
515			FIELDS = [{
516				var: "code",
517				type: "text-single",
518				label: "Your referral code",
519				required: true
520			}].freeze
521
522			def initialize(customer, tel, error: nil, finish: Finish, **)
523				@customer = customer
524				@tel = tel
525				@error = error
526				@finish = finish
527				@parent_code_repo = ParentCodeRepo.new(redis: REDIS, db: DB)
528			end
529
530			def add_form(reply)
531				form = reply.form
532				form.type = :form
533				form.title = "Enter Referral Code"
534				form.instructions = @error if @error
535				form.fields = FIELDS
536			end
537
538			def write
539				Command.reply { |reply|
540					reply.allowed_actions = [:next, :prev]
541					add_form(reply)
542				}.then(&method(:parse))
543			end
544
545			def parse(iq)
546				return Activation.for(@customer, nil, @tel).then(&:write) if iq.prev?
547
548				verify(iq.form.field("code")&.value&.to_s)
549					.catch_only(InvitesRepo::Invalid, &method(:invalid_code))
550					.then(&:write)
551			end
552
553		protected
554
555			def invalid_code(e)
556				InviteCode.new(@customer, @tel, error: e.message)
557			end
558
559			def customer_id
560				@customer.customer_id
561			end
562
563			def verify(code)
564				@parent_code_repo.find(code).then do |parent_customer_id|
565					if parent_customer_id
566						set_parent(parent_customer_id)
567					else
568						InvitesRepo.new(DB, REDIS).claim_code(customer_id, code) {
569							@customer.activate_plan_starting_now
570						}.then { Finish.new(@customer, @tel) }
571					end
572				end
573			end
574
575			def set_parent(parent_customer_id)
576				@customer = @customer.with_plan(
577					@customer.plan_name,
578					parent_customer_id: parent_customer_id
579				)
580				@customer.save_plan!.then do
581					self.class.for(@customer, @tel, finish: @finish)
582				end
583			end
584		end
585
586		class Mail
587			Payment.kinds[:mail] = method(:new)
588
589			def initialize(customer, tel, final_message: nil, **)
590				@customer = customer
591				@tel = tel
592				@final_message = final_message
593			end
594
595			def form
596				FormTemplate.render(
597					"registration/mail",
598					currency: @customer.currency,
599					final_message: @final_message,
600					**onboarding_extras
601				)
602			end
603
604			def onboarding_extras
605				jid = ProxiedJID.new(@customer.jid).unproxied
606				return {} unless jid.domain == CONFIG[:onboarding_domain]
607
608				{
609					customer_id: @customer.customer_id,
610					in_note: "Customer ID"
611				}
612			end
613
614			def write
615				Command.reply { |reply|
616					reply.allowed_actions = [:prev]
617					reply.status = :canceled
618					reply.command << form
619				}.then { |iq|
620					raise "Action not allowed" unless iq.prev?
621
622					Activation.for(@customer, nil, @tel).then(&:write)
623				}
624			end
625		end
626	end
627
628	class BillPlan
629		def initialize(customer, tel, finish: Finish)
630			@customer = customer
631			@tel = tel
632			@finish = finish
633		end
634
635		def write
636			@customer.bill_plan(note: "Bill #{@tel} for first month").then do
637				@finish.new(@customer, @tel).write
638			end
639		end
640	end
641
642	class Finish
643		def initialize(customer, tel)
644			@customer = customer
645			@tel = tel
646			@invites = InvitesRepo.new(DB, REDIS)
647		end
648
649		def write
650			@tel.order(DB, @customer).then(
651				->(_) { customer_active_tel_purchased },
652				method(:number_purchase_error)
653			)
654		end
655
656	protected
657
658		def number_purchase_error(e)
659			Command.log.error "number_purchase_error", e
660			TEL_SELECTIONS.delete(@customer.jid).then {
661				TEL_SELECTIONS[@customer.jid]
662			}.then { |choose|
663				choose.choose_tel(
664					error: "The JMP number #{@tel} is no longer available."
665				)
666			}.then { |tel| Finish.new(@customer, tel).write }
667		end
668
669		def raise_setup_error(e)
670			Command.log.error "@customer.register! failed", e
671			Command.finish(
672				"There was an error setting up your number, " \
673				"please contact JMP support.",
674				type: :error
675			)
676		end
677
678		def put_default_fwd
679			Bwmsgsv2Repo.new.put_fwd(@customer.customer_id, @tel.tel, CustomerFwd.for(
680				uri: "xmpp:#{@customer.jid}",
681				voicemail_enabled: true
682			))
683		end
684
685		def use_referral_code
686			@invites.use_pending_group_code(@customer.customer_id).then do |credit_to|
687				next unless credit_to
688
689				Transaction.new(
690					customer_id: @customer.customer_id,
691					transaction_id: "referral_#{@customer.customer_id}_#{credit_to}",
692					amount: @customer.monthly_price,
693					note: "Referral Bonus",
694					bonus_eligible?: false
695				).insert
696			end
697		end
698
699		def customer_active_tel_purchased
700			@customer.register!(@tel.tel).catch(&method(:raise_setup_error)).then {
701				EMPromise.all([
702					TEL_SELECTIONS.delete(@customer.jid),
703					put_default_fwd,
704					use_referral_code
705				])
706			}.then do
707				FinishOnboarding.for(@customer, @tel).then(&:write)
708			end
709		end
710	end
711
712	module FinishOnboarding
713		def self.for(customer, tel, db: LazyObject.new { DB })
714			jid = ProxiedJID.new(customer.jid).unproxied
715			if jid.domain == CONFIG[:onboarding_domain]
716				Snikket.for(customer, tel, db: db)
717			else
718				NotOnboarding.new(customer, tel)
719			end
720		end
721
722		class Snikket
723			def self.for(customer, tel, db:)
724				::Snikket::Repo.new(db: db).find_by_customer(customer).then do |is|
725					if is.empty?
726						new(customer, tel, db: db)
727					elsif is[0].bootstrap_token.empty?
728						# This is a need_dns one, try the launch again
729						new(customer, tel, db: db).launch(is[0].domain)
730					else
731						GetInvite.for(customer, is[0], tel, db: db)
732					end
733				end
734			end
735
736			def initialize(customer, tel, error: nil, old: nil, db:)
737				@customer = customer
738				@tel = tel
739				@error = error
740				@db = db
741				@old = old
742			end
743
744			ACTION_VAR = "http://jabber.org/protocol/commands#actions"
745
746			def form
747				FormTemplate.render(
748					"registration/snikket",
749					tel: @tel,
750					error: @error
751				)
752			end
753
754			def write
755				Command.reply { |reply|
756					reply.allowed_actions = [:next]
757					reply.command << form
758				}.then(&method(:next_step))
759			end
760
761			def next_step(iq)
762				subdomain = empty_nil(iq.form.field("subdomain")&.value)
763				domain = "#{subdomain}.snikket.chat"
764				if iq.form.field(ACTION_VAR)&.value == "custom_domain"
765					CustomDomain.new(@customer, @tel, old: @old).write
766				elsif @old && (!subdomain || domain == @old.domain)
767					GetInvite.for(@customer, @old, @tel, db: @db).then(&:write)
768				else
769					launch(domain)
770				end
771			end
772
773			def launch(domain)
774				IQ_MANAGER.write(::Snikket::Launch.new(
775					nil, CONFIG[:snikket_hosting_api], domain: domain
776				)).then { |launched|
777					save_instance_and_wait(domain, launched)
778				}.catch { |e|
779					next EMPromise.reject(e) unless e.respond_to?(:text)
780
781					Snikket.new(@customer, @tel, old: @old, error: e.text, db: @db).write
782				}
783			end
784
785			def save_instance_and_wait(domain, launched)
786				instance = ::Snikket::CustomerInstance.for(@customer, domain, launched)
787				repo = ::Snikket::Repo.new(db: @db)
788				(@old&.domain == domain ? EMPromise.resolve(nil) : repo.del(@old))
789					.then { repo.put(instance) }.then do
790						if launched.status == :needs_dns
791							NeedsDNS.new(@customer, instance, @tel, launched.records).write
792						else
793							GetInvite.for(@customer, instance, @tel, db: @db).then(&:write)
794						end
795					end
796			end
797
798			def empty_nil(s)
799				s.nil? || s.empty? ? nil : s
800			end
801
802			class NeedsDNS < Snikket
803				def initialize(customer, instance, tel, records, db: DB)
804					@customer = customer
805					@instance = instance
806					@tel = tel
807					@records = records
808					@db = db
809				end
810
811				def form
812					FormTemplate.render(
813						"registration/snikket_needs_dns",
814						records: @records
815					)
816				end
817
818				def write
819					Command.reply { |reply|
820						reply.allowed_actions = [:prev, :next]
821						reply.command << form
822					}.then do |iq|
823						if iq.prev?
824							CustomDomain.new(@customer, @tel, old: @instance).write
825						else
826							launch(@instance.domain)
827						end
828					end
829				end
830			end
831
832			class GetInvite
833				def self.for(customer, instance, tel, db: DB)
834					instance.fetch_invite.then do |xmpp_uri|
835						if xmpp_uri
836							GoToInvite.new(xmpp_uri)
837						else
838							new(customer, instance, tel, db: db)
839						end
840					end
841				end
842
843				def initialize(customer, instance, tel, db: DB)
844					@customer = customer
845					@instance = instance
846					@tel = tel
847					@db = db
848				end
849
850				def form
851					FormTemplate.render(
852						"registration/snikket_wait",
853						domain: @instance.domain
854					)
855				end
856
857				def write
858					Command.reply { |reply|
859						reply.allowed_actions = [:prev, :next]
860						reply.command << form
861					}.then do |iq|
862						if iq.prev?
863							Snikket.new(@customer, @tel, old: @instance, db: @db).write
864						else
865							GetInvite.for(@customer, @instance, @tel, db: @db).then(&:write)
866						end
867					end
868				end
869			end
870
871			class GoToInvite
872				def initialize(xmpp_uri)
873					@xmpp_uri = xmpp_uri
874				end
875
876				def write
877					Command.finish do |reply|
878						oob = OOB.find_or_create(reply.command)
879						oob.url = @xmpp_uri
880					end
881				end
882			end
883		end
884
885		class CustomDomain < Snikket
886			def initialize(customer, tel, old: nil, error: nil, db: DB)
887				@customer = customer
888				@tel = tel
889				@error = error
890				@old = old
891				@db = db
892			end
893
894			def form
895				FormTemplate.render(
896					"registration/snikket_custom",
897					tel: @tel,
898					error: @error
899				)
900			end
901
902			def write
903				Command.reply { |reply|
904					reply.allowed_actions = [:prev, :next]
905					reply.command << form
906				}.then do |iq|
907					if iq.prev?
908						Snikket.new(@customer, @tel, db: @db, old: @old).write
909					else
910						launch(empty_nil(iq.form.field("domain")&.value) || @old&.domain)
911					end
912				end
913			end
914		end
915
916		class NotOnboarding
917			def initialize(customer, tel)
918				@customer = customer
919				@tel = tel
920			end
921
922			def write
923				WelcomeMessage.new(@customer, @tel).welcome
924				Command.finish("Your JMP account has been activated as #{@tel}")
925			end
926		end
927	end
928end