~singpolyma/sgx-jmp

a688701e21e1cfccdc96f2639e0eaf40bd31dece — Stephen Paul Weber 6 months ago 8fd61ce + 8291b15
Merge branch 'activate-with-balance'

* activate-with-balance:
  If customer already has enough balance, just bill them and finish
  New BillPay registration step
  Save plan to DB as soon as it is selected
  Move credit_to lookup into relevant class
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