M lib/customer.rb => lib/customer.rb +2 -2
@@ 81,10 81,10 @@ class Customer
)
end
- def with_plan(plan_name)
+ def with_plan(plan_name, **kwargs)
self.class.new(
@customer_id, @jid,
- plan: @plan.with_plan_name(plan_name),
+ plan: @plan.with_plan_name(plan_name, **kwargs),
balance: @balance, tndetails: @tndetails, sgx: @sgx
)
end
M lib/customer_plan.rb => lib/customer_plan.rb +17 -2
@@ 70,15 70,29 @@ class CustomerPlan
:expired
end
- def with_plan_name(plan_name)
+ def with_plan_name(plan_name, **kwargs)
self.class.new(
@customer_id,
plan: Plan.for(plan_name),
- expires_at: @expires_at
+ expires_at: @expires_at, **kwargs
)
end
+ def verify_parent!
+ return unless @parent_customer_id
+
+ result = DB.query(<<~SQL, [@parent_customer_id])
+ SELECT plan_name FROM customer_plans WHERE customer_id=$1
+ SQL
+
+ raise "Invalid parent account" if !result || !result.first
+
+ plan = Plan.for(result.first["plan_name"])
+ raise "Parent currency mismatch" unless plan.currency == currency
+ end
+
def save_plan!
+ verify_parent!
DB.exec_defer(<<~SQL, [@customer_id, plan_name, @parent_customer_id])
INSERT INTO plan_log
(customer_id, plan_name, parent_customer_id, date_range)
@@ 107,6 121,7 @@ class CustomerPlan
end
def activate_plan_starting_now
+ verify_parent!
activated = DB.exec(<<~SQL, [@customer_id, plan_name, @parent_customer_id])
INSERT INTO plan_log (customer_id, plan_name, date_range, parent_customer_id)
VALUES ($1, $2, tsrange(LOCALTIMESTAMP, LOCALTIMESTAMP + '1 month'), $3)
M lib/registration.rb => lib/registration.rb +40 -11
@@ 11,6 11,7 @@ require_relative "./command"
require_relative "./em"
require_relative "./invites_repo"
require_relative "./oob"
+require_relative "./parent_code_repo"
require_relative "./proxied_jid"
require_relative "./tel_selections"
require_relative "./welcome_message"
@@ 105,7 106,7 @@ class Registration
def next_step(iq)
code = iq.form.field("code")&.value&.to_s
- save_customer_plan(iq).then {
+ save_customer_plan(iq, code).then {
finish_if_valid_invite(code)
}.catch_only(InvitesRepo::Invalid) do
@invites.stash_code(customer.customer_id, code).then do
@@ 124,10 125,12 @@ class Registration
end
end
- def save_customer_plan(iq)
- plan_name = iq.form.field("plan_name").value.to_s
- @customer = @customer.with_plan(plan_name)
- @customer.save_plan!
+ def save_customer_plan(iq, code)
+ ParentCodeRepo.new(REDIS).find(code).then do |parent|
+ plan_name = iq.form.field("plan_name").value.to_s
+ @customer = @customer.with_plan(plan_name, parent_customer_id: parent)
+ @customer.save_plan!
+ end
end
class GooglePlay
@@ 136,6 139,7 @@ class Registration
@google_play_userid = google_play_userid
@tel = tel
@invites = InvitesRepo.new(DB, REDIS)
+ @parent_code_repo = ParentCodeRepo.new(REDIS)
end
def used
@@ 163,17 167,25 @@ class Registration
end
def activate(iq)
- REDIS.sadd("google_play_userids", @google_play_userid).then {
- plan_name = iq.form.field("plan_name").value.to_s
- @customer = @customer.with_plan(plan_name)
- @customer.activate_plan_starting_now
+ plan_name = iq.form.field("plan_name").value
+ code = iq.form.field("code")&.value
+ EMPromise.all([
+ @parent_code_repo.find(code),
+ REDIS.sadd("google_play_userids", @google_play_userid)
+ ]).then { |(parent, _)|
+ save_active_plan(plan_name, parent)
}.then do
- use_referral_code(iq.form.field("code")&.value&.to_s)
+ use_referral_code(code)
end
end
protected
+ def save_active_plan(plan_name, parent)
+ @customer = @customer.with_plan(plan_name, parent_customer_id: parent)
+ @customer.activate_plan_starting_now
+ end
+
def use_referral_code(code)
EMPromise.resolve(nil).then {
@invites.claim_code(@customer.customer_id, code) {
@@ 406,7 418,24 @@ class Registration
end
class InviteCode
- Payment.kinds[:code] = method(:new)
+ Payment.kinds[:code] = ->(*args, **kw) { self.for(*args, **kw) }
+
+ def self.for(in_customer, tel, finish: Finish, **)
+ reload_customer(in_customer).then do |customer|
+ if customer.balance >= CONFIG[:activation_amount_accept]
+ next BillPlan.new(customer, tel, finish: finish)
+ end
+
+ msg = if customer.balance.positive?
+ "Account balance not enough to cover the activation"
+ end
+ new(customer, tel, error: msg)
+ end
+ end
+
+ def self.reload_customer(customer)
+ Command.execution.customer_repo.find(customer.customer_id)
+ end
FIELDS = [{
var: "code",
M test/test_customer.rb => test/test_customer.rb +5 -0
@@ 49,6 49,11 @@ class CustomerTest < Minitest::Test
em :test_bill_plan_activate
def test_bill_plan_reactivate_child
+ CustomerPlan::DB.expect(
+ :query,
+ [{ "plan_name" => "test_usd" }],
+ [String, ["parent"]]
+ )
CustomerPlan::DB.expect(:transaction, nil) do |&block|
block.call
true
M test/test_customer_repo.rb => test/test_customer_repo.rb +5 -0
@@ 195,6 195,11 @@ class CustomerRepoTest < Minitest::Test
["jmp_customer_feature_flags-testp"]
)
CustomerPlan::DB.expect(
+ :query,
+ [{ "plan_name" => "test_usd" }],
+ [String, ["1234"]]
+ )
+ CustomerPlan::DB.expect(
:exec_defer,
EMPromise.resolve(nil),
[String, ["testp", "test_usd", "1234"]]
M test/test_registration.rb => test/test_registration.rb +66 -9
@@ 112,7 112,9 @@ class RegistrationTest < Minitest::Test
class ActivationTest < Minitest::Test
Registration::Activation::DB = Minitest::Mock.new
- Registration::Activation::REDIS = FakeRedis.new
+ Registration::Activation::REDIS = FakeRedis.new(
+ "jmp_parent_codes" => { "PARENT_CODE" => 1 }
+ )
Registration::Activation::Payment = Minitest::Mock.new
Registration::Activation::Finish = Minitest::Mock.new
Command::COMMAND_MANAGER = Minitest::Mock.new
@@ 136,7 138,9 @@ class RegistrationTest < Minitest::Test
)
end]
)
- @customer.expect(:with_plan, @customer, ["test_usd"])
+ @customer.expect(:with_plan, @customer) do |*args, **|
+ assert_equal ["test_usd"], args
+ end
@customer.expect(:save_plan!, EMPromise.resolve(nil), [])
Registration::Activation::Payment.expect(
:for,
@@ 170,7 174,9 @@ class RegistrationTest < Minitest::Test
)
end]
)
- @customer.expect(:with_plan, @customer, ["test_usd"])
+ @customer.expect(:with_plan, @customer) do |*args, **|
+ assert_equal ["test_usd"], args
+ end
@customer.expect(:save_plan!, EMPromise.resolve(nil), [])
@customer.expect(:activate_plan_starting_now, EMPromise.resolve(nil), [])
Registration::Activation::DB.expect(:transaction, []) { |&blk| blk.call }
@@ 212,7 218,9 @@ class RegistrationTest < Minitest::Test
)
end]
)
- @customer.expect(:with_plan, @customer, ["test_usd"])
+ @customer.expect(:with_plan, @customer) do |*args, **|
+ assert_equal ["test_usd"], args
+ end
@customer.expect(:save_plan!, EMPromise.resolve(nil), [])
Registration::Activation::DB.expect(:transaction, []) { |&blk| blk.call }
Registration::Activation::DB.expect(
@@ 241,6 249,50 @@ class RegistrationTest < Minitest::Test
assert_mock Registration::Activation::DB
end
em :test_write_with_group_code
+
+ def test_write_with_parent_code
+ Command::COMMAND_MANAGER.expect(
+ :write,
+ EMPromise.resolve(Blather::Stanza::Iq::Command.new.tap { |iq|
+ iq.form.fields = [
+ { var: "plan_name", value: "test_usd" },
+ { var: "code", value: "PARENT_CODE" }
+ ]
+ }),
+ [Matching.new do |iq|
+ assert_equal :form, iq.form.type
+ assert_equal(
+ "You've selected +15555550000 as your JMP number.",
+ iq.form.instructions.lines.first.chomp
+ )
+ end]
+ )
+ @customer.expect(:with_plan, @customer) do |*args, **kwargs|
+ assert_equal ["test_usd"], args
+ assert_equal({ parent_customer_id: 1 }, kwargs)
+ end
+ @customer.expect(:save_plan!, EMPromise.resolve(nil), [])
+ Registration::Activation::DB.expect(:transaction, []) { |&blk| blk.call }
+ Registration::Activation::DB.expect(
+ :exec,
+ OpenStruct.new(cmd_tuples: 0),
+ [String, ["test", "PARENT_CODE"]]
+ )
+ Registration::Activation::Payment.expect(
+ :for,
+ EMPromise.reject(:test_result),
+ [Blather::Stanza::Iq, @customer, "+15555550000"]
+ )
+ assert_equal(
+ :test_result,
+ execute_command { @activation.write.catch { |e| e } }
+ )
+ assert_mock Command::COMMAND_MANAGER
+ assert_mock @customer
+ assert_mock Registration::Activation::Payment
+ assert_mock Registration::Activation::DB
+ end
+ em :test_write_with_parent_code
end
class AllowTest < Minitest::Test
@@ 426,13 478,18 @@ class RegistrationTest < Minitest::Test
{ var: "activation_method", value: "code" },
{ var: "plan_name", value: "test_usd" }
]
- result = Registration::Payment.for(
- iq,
- customer,
- "+15555550000"
- )
+ cust = customer
+ result = execute_command do
+ Command.execution.customer_repo.expect(:find, cust, ["test"])
+ Registration::Payment.for(
+ iq,
+ cust,
+ "+15555550000"
+ )
+ end
assert_kind_of Registration::Payment::InviteCode, result
end
+ em :test_for_code
class BitcoinTest < Minitest::Test
Registration::Payment::Bitcoin::BTC_SELL_PRICES = Minitest::Mock.new