# frozen_string_literal: true
require "erb"
require_relative "./oob"
class Registration
def self.for(iq, customer, web_register_manager)
EMPromise.resolve(customer&.registered?).then do |registered|
if registered
Registered.new(iq, registered.phone)
else
web_register_manager.choose_tel(iq).then do |(riq, tel)|
Activation.for(riq, customer, tel)
end
end
end
end
class Registered
def initialize(iq, tel)
@reply = iq.reply
@reply.status = :completed
@tel = tel
end
def write
@reply.note_type = :error
@reply.note_text = <<~NOTE
You are already registered with JMP number #{@tel}
NOTE
BLATHER << @reply
nil
end
end
class Activation
def self.for(iq, customer, tel)
if customer&.active?
Finish.new(iq, customer, tel)
elsif customer
EMPromise.resolve(new(iq, customer, tel))
else
# Create customer_id
raise "TODO"
end
end
def initialize(iq, customer, tel)
@reply = iq.reply
reply.allowed_actions = [:next]
@customer = customer
@tel = tel
end
attr_reader :reply, :customer, :tel
FORM_FIELDS = [
{
var: "activation_method",
type: "list-single",
label: "Activate using",
required: true,
options: [
{
value: "bitcoin",
label: "Bitcoin"
},
{
value: "credit_card",
label: "Credit Card ($#{CONFIG[:activation_amount]})"
},
{
value: "code",
label: "Referral or Activation Code"
}
]
},
{
var: "plan_name",
type: "list-single",
label: "What currency should your account balance be in?",
required: true,
options: [
{
value: "cad_beta_unlimited-v20210223",
label: "Canadian Dollars"
},
{
value: "usd_beta_unlimited-v20210223",
label: "United States Dollars"
}
]
}
].freeze
def write
rate_center.then do |center|
form = reply.form
form.type = :form
form.title = "Activate JMP"
form.instructions = "Going to activate #{tel} (#{center})"
form.fields = FORM_FIELDS
COMMAND_MANAGER.write(reply).then { |iq|
Payment.for(iq, customer, tel)
}.then(&:write)
end
end
protected
def rate_center
EM.promise_fiber do
center = BandwidthIris::Tn.get(tel).get_rate_center
"#{center[:rate_center]}, #{center[:state]}"
end
end
end
module Payment
def self.kinds
@kinds ||= {}
end
def self.for(iq, customer, tel)
plan_name = iq.form.field("plan_name").value.to_s
customer = customer.with_plan(plan_name)
kinds.fetch(iq.form.field("activation_method")&.value&.to_s&.to_sym) {
raise "Invalid activation method"
}.call(iq, customer, tel)
end
class Bitcoin
Payment.kinds[:bitcoin] = method(:new)
def initialize(iq, customer, tel)
@reply = iq.reply
reply.note_type = :info
reply.status = :completed
@customer = customer
@customer_id = customer.customer_id
@tel = tel
@addr = ELECTRUM.createnewaddress
end
attr_reader :reply, :customer_id, :tel
def save
EMPromise.all([
REDIS.mset(
"pending_tel_for-#{customer_id}", tel,
"pending_plan_for-#{customer_id}", @customer.plan_name
),
@addr.then do |addr|
REDIS.sadd("jmp_customer_btc_addresses-#{customer_id}", addr)
end
])
end
def note_text(amount, addr)
<<~NOTE
Activate your account by sending at least #{'%.6f' % amount} BTC to
#{addr}
You will receive a notification when your payment is complete.
NOTE
end
def write
EMPromise.all([
@addr,
save,
BTC_SELL_PRICES.public_send(@customer.currency.to_s.downcase)
]).then do |(addr, _, rate)|
min = CONFIG[:activation_amount] / rate
reply.note_text = note_text(min, addr)
BLATHER << reply
nil
end
end
end
class CreditCard
Payment.kinds[:credit_card] = ->(*args) { self.for(*args) }
def self.for(iq, customer, tel)
customer.payment_methods.then do |payment_methods|
if (method = payment_methods.default_payment_method)
Activate.new(iq, customer, method, tel)
else
new(iq, customer, tel)
end
end
end
def initialize(iq, customer, tel)
@customer = customer
@tel = tel
@reply = iq.reply
@reply.allowed_actions = [:next]
@reply.note_type = :info
@reply.note_text = "#{oob.desc}: #{oob.url}"
end
attr_reader :reply
def oob
oob = OOB.find_or_create(@reply.command)
oob.url = CONFIG[:credit_card_url].call(
@reply.to.stripped.to_s,
@customer.customer_id
)
oob.desc = "Add credit card, then return here and choose next"
oob
end
def write
COMMAND_MANAGER.write(@reply).then do |riq|
CreditCard.for(riq, @customer, @tel).write
end
end
class Activate
def initialize(iq, customer, payment_method, tel)
@iq = iq
@customer = customer
@payment_method = payment_method
@tel = tel
end
def write
Transaction.sale(
@customer,
CONFIG[:activation_amount],
@payment_method
).then(
method(:sold),
->(_) { declined }
)
end
protected
def sold(tx)
tx.insert.then {
@customer.bill_plan
}.then do
Finish.new(@iq, @customer, @tel).write
end
end
DECLINE_MESSAGE =
"Your bank declined the transaction. " \
"Often this happens when a person's credit card " \
"is a US card that does not support international " \
"transactions, as JMP is not based in the USA, though " \
"we do support transactions in USD.\n\n" \
"If you were trying a prepaid card, you may wish to use "\
"Privacy.com instead, as they do support international " \
"transactions.\n\n " \
"You may add another card and then choose next"
def decline_oob(reply)
oob = OOB.find_or_create(reply.command)
oob.url = CONFIG[:credit_card_url].call(
reply.to.stripped.to_s,
@customer.customer_id
)
oob.desc = DECLINE_MESSAGE
oob
end
def declined
reply = @iq.reply
reply_oob = decline_oob(reply)
reply.allowed_actions = [:next]
reply.note_type = :error
reply.note_text = "#{reply_oob.desc}: #{reply_oob.url}"
COMMAND_MANAGER.write(reply).then do |riq|
CreditCard.for(riq, @customer, @tel).write
end
end
end
end
end
class Finish
def initialize(iq, customer, tel)
@reply = iq.reply
@reply.status = :completed
@reply.note_type = :info
@reply.note_text = "Your JMP account has been activated as #{tel}"
@customer = customer
@tel = tel
end
def write
BandwidthTNOrder.create(@tel).then(&:poll).then(
->(_) { customer_active_tel_purchased },
lambda do |_|
@reply.note_type = :error
@reply.note_text =
"The JMP number #{@tel} is no longer available, " \
"please visit https://jmp.chat and choose another."
BLATHER << @reply
end
)
end
protected
def cheogram_sip_addr
"sip:#{ERB::Util.url_encode(@reply.to.stripped.to_s)}@sip.cheogram.com"
end
def customer_active_tel_purchased
@customer.register!(@tel).then {
EMPromise.all([
REDIS.set("catapult_fwd-#{@tel}", cheogram_sip_addr),
REDIS.set(
"catapult_fwd_timeout-#{@reply.to.stripped}",
25 # ~5 seconds / ring, 5 rings
)
])
}.then { BLATHER << @reply }
end
end
end