M config-schema.dhall => config-schema.dhall +1 -0
@@ 1,4 1,5 @@
{ activation_amount : Natural
+, activation_amount_accept : Natural
, admins : List Text
, adr : Text
, approved_domains : List { mapKey : Text, mapValue : Optional Text }
M config.dhall.sample => config.dhall.sample +1 -0
@@ 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) ->
M lib/customer.rb => lib/customer.rb +1 -1
@@ 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
M lib/customer_plan.rb => lib/customer_plan.rb +23 -3
@@ 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
M lib/registration.rb => lib/registration.rb +30 -17
@@ 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
M test/test_customer.rb => test/test_customer.rb +5 -0
@@ 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
M test/test_helper.rb => test/test_helper.rb +1 -0
@@ 68,6 68,7 @@ CONFIG = {
},
notify_from: "notify_from@example.org",
activation_amount: 1,
+ activation_amount_accept: 1,
plans: [
{
name: "test_usd",
M test/test_registration.rb => test/test_registration.rb +13 -6
@@ 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