M lib/customer.rb => lib/customer.rb +47 -0
@@ 52,6 52,14 @@ class Customer
)
end
+ def bill_plan
+ EM.promise_fiber do
+ DB.transaction do
+ charge_for_plan
+ add_one_month_to_current_plan unless activate_plan_starting_now
+ end
+ end
+ end
def payment_methods
@payment_methods ||=
@@ 72,4 80,43 @@ class Customer
result&.respond_to?(:registered?) && result&.registered?
end
end
+
+protected
+
+ def charge_for_plan
+ params = [
+ @customer_id,
+ "#{@customer_id}-bill-#{plan_name}-at-#{Time.now.to_i}",
+ -@plan.monthly_price
+ ]
+ DB.exec(<<~SQL, params)
+ INSERT INTO transactions
+ (customer_id, transaction_id, created_at, amount)
+ VALUES ($1, $2, LOCALTIMESTAMP, $3)
+ SQL
+ 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)
+ VALUES ($1, $2, tsrange(LOCALTIMESTAMP, LOCALTIMESTAMP + '1 month'))
+ ON CONFLICT DO NOTHING
+ SQL
+ end
+
+ def add_one_month_to_current_plan
+ DB.exec(<<~SQL, [@customer_id])
+ UPDATE plan_log SET date_range=range_merge(
+ date_range,
+ tsrange(
+ LOCALTIMESTAMP,
+ GREATEST(upper(date_range), LOCALTIMESTAMP) + '1 month'
+ )
+ )
+ WHERE
+ customer_id=$1 AND
+ date_range && tsrange(LOCALTIMESTAMP, LOCALTIMESTAMP + '1 month')
+ SQL
+ end
end
M lib/plan.rb => lib/plan.rb +4 -0
@@ 20,6 20,10 @@ class Plan
@plan[:currency]
end
+ def monthly_price
+ BigDecimal.new(@plan[:monthly_price]) / 1000
+ end
+
def merchant_account
CONFIG[:braintree][:merchant_accounts].fetch(currency) do
raise "No merchant account for this currency"
M test/test_customer.rb => test/test_customer.rb +55 -0
@@ 47,4 47,59 @@ class CustomerTest < Minitest::Test
assert_equal BigDecimal.new(0), customer.balance
end
em :test_for_customer_id_not_found
+
+ def test_bill_plan_activate
+ Customer::DB.expect(:transaction, nil) do |&block|
+ block.call
+ true
+ end
+ Customer::DB.expect(
+ :exec,
+ nil,
+ [
+ String,
+ Matching.new do |params|
+ params[0] == "test" &&
+ params[1].is_a?(String) &&
+ BigDecimal.new(-1) == params[2]
+ end
+ ]
+ )
+ Customer::DB.expect(
+ :exec,
+ OpenStruct.new(cmd_tuples: 1),
+ [String, ["test", "test_usd"]]
+ )
+ Customer.new("test", plan_name: "test_usd").bill_plan.sync
+ Customer::DB.verify
+ end
+ em :test_bill_plan_activate
+
+ def test_bill_plan_update
+ Customer::DB.expect(:transaction, nil) do |&block|
+ block.call
+ true
+ end
+ Customer::DB.expect(
+ :exec,
+ nil,
+ [
+ String,
+ Matching.new do |params|
+ params[0] == "test" &&
+ params[1].is_a?(String) &&
+ BigDecimal.new(-1) == params[2]
+ end
+ ]
+ )
+ Customer::DB.expect(
+ :exec,
+ OpenStruct.new(cmd_tuples: 0),
+ [String, ["test", "test_usd"]]
+ )
+ Customer::DB.expect(:exec, nil, [String, ["test"]])
+ Customer.new("test", plan_name: "test_usd").bill_plan.sync
+ Customer::DB.verify
+ end
+ em :test_bill_plan_update
end