diff --git a/config-schema.dhall b/config-schema.dhall index fd5ab67b2964c0bca619510e27e1b9d654f2381e..1e6f5eda9b7a7e1fcc9d441b3180cfb6015467bc 100644 --- a/config-schema.dhall +++ b/config-schema.dhall @@ -1,4 +1,5 @@ { activation_amount : Natural +, activation_amount_accept : Natural , admins : List Text , adr : Text , approved_domains : List { mapKey : Text, mapValue : Optional Text } diff --git a/config.dhall.sample b/config.dhall.sample index 3e19598df5cd8ad7565ccb03d4938dae6ef9dfd9..61b0e87b7696af2d85c995a912f0ed2ac43293dc 100644 --- a/config.dhall.sample +++ b/config.dhall.sample @@ -66,6 +66,7 @@ in }, oxr_app_id = "", activation_amount = 15, + activation_amount_accept = 15, credit_card_url = \(jid: Text) -> \(customer_id: Text) -> "https://pay.jmp.chat/${jid}/credit_cards?customer_id=${customer_id}", electrum_notify_url = \(address: Text) -> \(customer_id: Text) -> diff --git a/lib/customer.rb b/lib/customer.rb index af54dd0ce16ad9a1f084727dbadb99a4dec70e8b..f837ed7f4239b8a73aad26f35f8e07467addcc92 100644 --- a/lib/customer.rb +++ b/lib/customer.rb @@ -25,7 +25,7 @@ class Customer def_delegators :@plan, :active?, :activate_plan_starting_now, :bill_plan, :currency, :merchant_account, :plan_name, :minute_limit, :message_limit, :auto_top_up_amount, :monthly_overage_limit, - :monthly_price + :monthly_price, :save_plan! def_delegators :@sgx, :register!, :registered?, :set_ogm_url, :fwd, :transcription_enabled def_delegators :@usage, :usage_report, :message_usage, :incr_message_usage diff --git a/lib/customer_plan.rb b/lib/customer_plan.rb index 86fd8e2a1cdd04abbb793c94b423c666a53d1bfd..2dbaf0cbda3418e5286791c3addbd48d3cbf373e 100644 --- a/lib/customer_plan.rb +++ b/lib/customer_plan.rb @@ -44,6 +44,21 @@ class CustomerPlan ) end + def save_plan! + DB.exec_defer(<<~SQL, [@customer_id, plan_name]) + INSERT INTO plan_log + (customer_id, plan_name, date_range) + VALUES ( + $1, + $2, + tsrange( + LOCALTIMESTAMP - '2 seconds'::interval, + LOCALTIMESTAMP - '1 second'::interval + ) + ) + SQL + end + def bill_plan EM.promise_fiber do DB.transaction do @@ -54,12 +69,17 @@ class CustomerPlan end def activate_plan_starting_now - DB.exec(<<~SQL, [@customer_id, plan_name]).cmd_tuples.positive? - INSERT INTO plan_log - (customer_id, plan_name, date_range) + activated = DB.exec(<<~SQL, [@customer_id, plan_name]).cmd_tuples.positive? + INSERT INTO plan_log (customer_id, plan_name, date_range) VALUES ($1, $2, tsrange(LOCALTIMESTAMP, LOCALTIMESTAMP + '1 month')) ON CONFLICT DO NOTHING SQL + return false unless activated + + DB.exec(<<~SQL, [@customer_id]) + DELETE FROM plan_log WHERE customer_id=$1 AND date_range << '[now,now]' + AND upper(date_range) - lower(date_range) < '2 seconds' + SQL end def activation_date diff --git a/lib/registration.rb b/lib/registration.rb index 849d28e60461e07c7b9a32bc197e02059697a38f..ca21c8062c0bab1f2deb97cc0470baf36e5cfd41 100644 --- a/lib/registration.rb +++ b/lib/registration.rb @@ -38,9 +38,10 @@ class Registration jid = ProxiedJID.new(customer.jid).unproxied if customer.active? Finish.new(customer, tel) + elsif customer.balance >= CONFIG[:activation_amount_accept] + BillPlan.new(customer, tel) elsif CONFIG[:approved_domains].key?(jid.domain.to_sym) - credit_to = CONFIG[:approved_domains][jid.domain.to_sym] - Allow.new(customer, tel, credit_to) + Allow.for(customer, tel, jid) else new(customer, tel) end @@ -86,6 +87,11 @@ class Registration end class Allow < Activation + def self.for(customer, tel, jid) + credit_to = CONFIG[:approved_domains][jid.domain.to_sym] + new(customer, tel, credit_to) + end + def initialize(customer, tel, credit_to) super(customer, tel) @credit_to = credit_to @@ -132,9 +138,11 @@ class Registration def self.for(iq, customer, tel, final_message: nil, finish: Finish) 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(customer, tel, final_message: final_message, finish: finish) + customer.save_plan!.then do + kinds.fetch(iq.form.field("activation_method")&.value&.to_s&.to_sym) { + raise "Invalid activation method" + }.call(customer, tel, final_message: final_message, finish: finish) + end end class Bitcoin @@ -152,14 +160,7 @@ class Registration attr_reader :customer_id, :tel def save - EMPromise.all([ - REDIS.setex("pending_tel_for-#{@customer.jid}", THIRTY_DAYS, tel), - REDIS.setex( - "pending_plan_for-#{customer_id}", - THIRTY_DAYS, - @customer.plan_name - ) - ]) + REDIS.setex("pending_tel_for-#{@customer.jid}", THIRTY_DAYS, tel) end def note_text(amount, addr) @@ -254,10 +255,8 @@ class Registration protected def sold(tx) - tx.insert.then { - @customer.bill_plan - }.then do - @finish.new(@customer, @tel).write + tx.insert.then do + BillPlan.new(@customer, @tel, finish: @finish).write end end @@ -408,6 +407,20 @@ class Registration end end + class BillPlan + def initialize(customer, tel, finish: Finish) + @customer = customer + @tel = tel + @finish = finish + end + + def write + @customer.bill_plan.then do + @finish.new(@customer, @tel).write + end + end + end + class Finish def initialize(customer, tel) @customer = customer diff --git a/test/test_customer.rb b/test/test_customer.rb index 360cf4876336e45130d676f7244c4fa993de70a5..43442dda07fdc64696bd2fd386e6cf9c58650a03 100644 --- a/test/test_customer.rb +++ b/test/test_customer.rb @@ -38,6 +38,11 @@ class CustomerTest < Minitest::Test OpenStruct.new(cmd_tuples: 1), [String, ["test", "test_usd"]] ) + CustomerPlan::DB.expect( + :exec, + OpenStruct.new(cmd_tuples: 0), + [String, ["test"]] + ) customer(plan_name: "test_usd").bill_plan.sync CustomerPlan::DB.verify end diff --git a/test/test_helper.rb b/test/test_helper.rb index f397d1555d0275e6d0e5671843551219f73db4ad..981decc44f4161de852b4cf79386c510f1100a92 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -68,6 +68,7 @@ CONFIG = { }, notify_from: "notify_from@example.org", activation_amount: 1, + activation_amount_accept: 1, plans: [ { name: "test_usd", diff --git a/test/test_registration.rb b/test/test_registration.rb index 08b18fc0f5f30f3517d6149c381ea9698b30bcd6..a6b8cc6572bc4c0fd98bd4f79c9b21140e2a2f25 100644 --- a/test/test_registration.rb +++ b/test/test_registration.rb @@ -245,10 +245,8 @@ class RegistrationTest < Minitest::Test def test_for_bitcoin cust = Minitest::Mock.new(customer) - cust.expect( - :add_btc_address, - EMPromise.resolve("testaddr") - ) + cust.expect(:with_plan, cust, ["test_usd"]) + cust.expect(:save_plan!, nil) iq = Blather::Stanza::Iq::Command.new iq.form.fields = [ { var: "activation_method", value: "bitcoin" }, @@ -256,9 +254,13 @@ class RegistrationTest < Minitest::Test ] result = Registration::Payment.for(iq, cust, "+15555550000") assert_kind_of Registration::Payment::Bitcoin, result + assert_mock cust end def test_for_credit_card + cust = Minitest::Mock.new(customer) + cust.expect(:with_plan, cust, ["test_usd"]) + cust.expect(:save_plan!, nil) braintree_customer = Minitest::Mock.new CustomerFinancials::BRAINTREE.expect( :customer, @@ -277,14 +279,18 @@ class RegistrationTest < Minitest::Test ] result = Registration::Payment.for( iq, - customer, + cust, "+15555550000" ).sync assert_kind_of Registration::Payment::CreditCard, result + assert_mock cust end em :test_for_credit_card def test_for_code + cust = Minitest::Mock.new(customer) + cust.expect(:with_plan, cust, ["test_usd"]) + cust.expect(:save_plan!, nil) iq = Blather::Stanza::Iq::Command.new iq.form.fields = [ { var: "activation_method", value: "code" }, @@ -292,10 +298,11 @@ class RegistrationTest < Minitest::Test ] result = Registration::Payment.for( iq, - customer, + cust, "+15555550000" ) assert_kind_of Registration::Payment::InviteCode, result + assert_mock cust end class BitcoinTest < Minitest::Test