@@ -16,6 +16,12 @@ require_relative "./proxied_jid"
require_relative "./tel_selections"
require_relative "./welcome_message"
+def reload_customer(customer)
+ EMPromise.resolve(nil).then do
+ Command.execution.customer_repo.find(customer.customer_id)
+ end
+end
+
class Registration
def self.for(customer, google_play_userid, tel_selections)
if (reg = customer.registered?)
@@ -272,10 +278,11 @@ class Registration
raise NotImplementedError, "Subclass must implement"
end
- def initialize(customer, tel, **)
+ def initialize(customer, tel, maybe_bill: MaybeBill, **)
@customer = customer
@customer_id = customer.customer_id
@tel = tel
+ @maybe_bill = maybe_bill
end
def save
@@ -285,11 +292,9 @@ class Registration
attr_reader :customer_id, :tel
def form(rate, addr)
- amount = CONFIG[:activation_amount] / rate
-
FormTemplate.render(
reg_form_name,
- amount: amount,
+ amount: @maybe_bill.price(@tel) / rate,
addr: addr
)
end
@@ -372,7 +377,7 @@ class Registration
end
def call
- reload_customer.then do |customer|
+ reload_customer(@customer).then do |customer|
if customer.balance >= CONFIG[:activation_amount_accept]
next BillPlan.new(customer, @tel, finish: @finish)
end
@@ -381,19 +386,19 @@ class Registration
end
end
- def reload_customer
- EMPromise.resolve(nil).then do
- Command.execution.customer_repo.find(@customer.customer_id)
- end
- end
-
def self.bill?
true
end
+
+ # @return [Float] The price of the number + activation fee
+ # @param [TelSelection::ChooseTel::Tn] tel The phone number to charge
+ def self.price(tel)
+ CONFIG[:activation_amount] + tel.price
+ end
end
class JustCharge
- def initialize(customer)
+ def initialize(customer, *, **)
@customer = customer
end
@@ -406,25 +411,24 @@ class Registration
end
end
+ def self.price(tel)
+ tel.price
+ end
+
class CreditCard
Payment.kinds[:credit_card] = ->(*args, **kw) { self.for(*args, **kw) }
def self.for(in_customer, tel, finish: Finish, maybe_bill: MaybeBill, **)
maybe_bill.new(in_customer, tel, finish: finish).call do |customer|
- new(customer, tel, finish: finish)
+ new(customer, tel, finish: finish, maybe_bill: maybe_bill)
end
end
- def self.reload_customer(customer)
- EMPromise.resolve(nil).then do
- Command.execution.customer_repo.find(customer.customer_id)
- end
- end
-
- def initialize(customer, tel, finish: Finish)
+ def initialize(customer, tel, finish: Finish, maybe_bill: MaybeBill)
@customer = customer
@tel = tel
@finish = finish
+ @maybe_bill = maybe_bill
end
def oob(reply)
@@ -432,7 +436,7 @@ class Registration
oob.url = CONFIG[:credit_card_url].call(
reply.to.stripped.to_s.gsub("\\", "%5C"),
@customer.customer_id
- ) + "&amount=#{CONFIG[:activation_amount]}"
+ ) + "&amount=#{@maybe_bill.price(@tel).ceil}"
oob.desc = "Pay by credit card, save, then next here to continue"
oob
end
@@ -446,9 +450,15 @@ class Registration
}.then do |iq|
next Activation.for(@customer, nil, @tel).then(&:write) if iq.prev?
- CreditCard.for(@customer, @tel, finish: @finish).then(&:write)
+ try_again
end
end
+
+ def try_again
+ CreditCard.for(
+ @customer, @tel, finish: @finish, maybe_bill: @maybe_bill
+ ).then(&:write)
+ end
end
class InviteCode
@@ -537,11 +547,10 @@ class Registration
end
def form
- price = @maybe_bill.bill? ? CONFIG[:activation_amount] + @tel.price : @tel.price
FormTemplate.render(
"registration/mail",
currency: @customer.currency,
- price: price,
+ price: @maybe_bill.price(@tel),
**onboarding_extras
)
end
@@ -579,7 +588,9 @@ class Registration
def write
@customer.bill_plan(note: "Bill #{@tel} for first month").then do
- @finish.new(@customer, @tel).write
+ updated_customer =
+ @customer.with_balance(@customer.balance - @customer.monthly_price)
+ @finish.new(updated_customer, @tel).write
end
end
end
@@ -592,12 +603,42 @@ class Registration
end
def write
- @tel.order(DB, @customer).then(
- ->(_) { customer_active_tel_purchased },
- method(:number_purchase_error)
+ if @customer.balance >= @tel.price
+ @tel.order(DB, @customer).then(
+ ->(_) { customer_active_tel_purchased },
+ method(:number_purchase_error)
+ )
+ else
+ buy_number.then {
+ try_again
+ }.then(&:write)
+ end
+ end
+
+ def try_again
+ reload_customer(@customer).then do |customer|
+ Finish.new(customer, @tel)
+ end
+ end
+
+ def form
+ FormTemplate.render(
+ "registration/buy_number",
+ tel: @tel
)
end
+ def buy_number
+ Command.reply { |reply|
+ reply.command << form
+ }.then { |iq|
+ Payment.for(
+ iq, @customer, @tel,
+ maybe_bill: ::Registration::Payment::JustCharge
+ ).write
+ }
+ end
+
protected
def number_purchase_error(e)
@@ -646,7 +687,8 @@ class Registration
EMPromise.all([
TEL_SELECTIONS.delete(@customer.jid),
put_default_fwd,
- use_referral_code
+ use_referral_code,
+ @tel.charge(@customer)
])
}.then do
FinishOnboarding.for(@customer, @tel).then(&:write)
@@ -1684,6 +1684,144 @@ class RegistrationTest < Minitest::Test
end
em :test_write_local_inventory
+ def test_write_local_inventory_must_pay
+ low_cust = customer(
+ sgx: @sgx,
+ jid: Blather::JID.new("test\\40onboarding.example.com@proxy")
+ ).with_balance(5.0)
+ high_cust = customer(
+ sgx: @sgx,
+ jid: Blather::JID.new("test\\40onboarding.example.com@proxy")
+ ).with_balance(100.0)
+
+ stub_request(
+ :post,
+ "https://dashboard.bandwidth.com/v1.0/accounts/moveto/moveTns"
+ ).with(
+ body: {
+ CustomerOrderId: "test",
+ SourceAccountId: "bandwidth_account_id",
+ SiteId: "test_site",
+ SipPeerId: "test_peer",
+ TelephoneNumbers: { TelephoneNumber: "5555550000" }
+ }.to_xml(indent: 0, root: "MoveTnsOrder")
+ ).to_return(status: 200, body: "", headers: {})
+
+ Registration::Finish::REDIS.expect(
+ :get,
+ nil,
+ ["jmp_customer_pending_invite-test"]
+ )
+ Registration::Finish::REDIS.expect(
+ :del,
+ nil,
+ ["jmp_customer_pending_invite-test"]
+ )
+ Registration::Finish::REDIS.expect(
+ :hget,
+ nil,
+ ["jmp_group_codes", nil]
+ )
+ Registration::Finish::DB.expect(
+ :exec_defer,
+ EMPromise.resolve(OpenStruct.new(cmd_tuples: 1)),
+ [String, ["+15555550000"]]
+ )
+ Bwmsgsv2Repo::REDIS.expect(
+ :get,
+ EMPromise.resolve(nil),
+ ["jmp_customer_backend_sgx-test"]
+ )
+ Bwmsgsv2Repo::REDIS.expect(
+ :set,
+ nil,
+ [
+ "catapult_fwd-+15555550000",
+ "xmpp:test\\40onboarding.example.com@proxy"
+ ]
+ )
+ Bwmsgsv2Repo::REDIS.expect(
+ :set,
+ nil,
+ ["catapult_fwd_timeout-customer_test@component", 25]
+ )
+
+ local_tel = TelSelections::ChooseTel::Tn::LocalInventory.new(
+ TelSelections::ChooseTel::Tn.new("+15555550000"),
+ "bandwidth_account_id",
+ price: 10.0
+ )
+
+ Registration::Payment::CreditCard.stub(
+ :new,
+ OpenStruct.new(
+ write: lambda {
+ # simulates successful try_again
+ Registration::Payment::CreditCard.for(
+ high_cust,
+ local_tel,
+ # we know maybe_bill will be passed as JustCharge
+ # since that's hardcoded
+ maybe_bill: Registration::Payment::JustCharge
+ )
+ }
+ )
+ ) do
+ result = execute_command do
+ @sgx.expect(
+ :register!,
+ EMPromise.resolve(@sgx.with(
+ registered?: Blather::Stanza::Iq::IBR.new.tap do |ibr|
+ ibr.phone = "+15555550000"
+ end
+ )),
+ ["+15555550000"]
+ )
+
+ Command::COMMAND_MANAGER.expect(
+ :write,
+ EMPromise.resolve(Blather::Stanza::Iq::Command.new.tap { |iq|
+ iq.form.fields = [
+ { var: "activation_method", value: "credit_card" },
+ { var: "plan_name", value: "test_usd" }
+ ]
+ }),
+ [Matching.new do |iq|
+ assert_equal :form, iq.form.type
+ assert iq.form.field("activation_method")
+ assert iq.form.field("plan_name")
+ end]
+ )
+
+ Command.execution.customer_repo.expect(
+ :find,
+ EMPromise.resolve(high_cust),
+ ["test"]
+ )
+
+ Command::COMMAND_MANAGER.expect(
+ :write,
+ EMPromise.reject(:test_result),
+ [Matching.new do |iq|
+ assert_equal :form, iq.form.type
+ assert iq.form.field("subdomain")
+ end]
+ )
+
+ Registration::Finish.new(
+ low_cust,
+ local_tel
+ ).write.catch { |e| e }
+ end
+ assert_equal :test_result, result
+ assert_mock @sgx
+ assert_mock Registration::Finish::REDIS
+ assert_mock Bwmsgsv2Repo::REDIS
+ assert_mock Command::COMMAND_MANAGER
+ end
+ end
+ em :test_write_local_inventory_must_pay
+
def test_write_tn_fail
create_order = stub_request(
:post,