~singpolyma/sgx-jmp

27be78a12fa0b03846e4d8cbf857d095ccb95c29 — Stephen Paul Weber 1 year, 2 months ago 2e7f0c4
Save plan to DB as soon as it is selected

Customer will appear as instantly expired (having been active for 1 second in
the immediate past).

This can replace the pending_plan key for BTC activations. It also serves as a
record for mail-in activation of what plan they had selected.

Once the account is really activated the tiny row is removed.
5 files changed, 48 insertions(+), 21 deletions(-)

M lib/customer.rb
M lib/customer_plan.rb
M lib/registration.rb
M test/test_customer.rb
M test/test_registration.rb
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 +6 -11
@@ 136,9 136,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


@@ 156,14 158,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)

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_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