M lib/customer.rb => lib/customer.rb +8 -28
@@ 4,10 4,11 @@ require "forwardable"
require_relative "./api"
require_relative "./blather_ext"
-require_relative "./customer_info"
-require_relative "./customer_ogm"
-require_relative "./customer_plan"
require_relative "./customer_usage"
+require_relative "./customer_plan"
+require_relative "./customer_ogm"
+require_relative "./customer_info"
+require_relative "./customer_finacials"
require_relative "./backend_sgx"
require_relative "./ibr"
require_relative "./payment_methods"
@@ 27,6 28,9 @@ class Customer
def_delegators :@sgx, :register!, :registered?, :set_ogm_url,
:fwd, :transcription_enabled
def_delegators :@usage, :usage_report, :message_usage, :incr_message_usage
+ def_delegators :@financials, :payment_methods, :btc_addresses,
+ :add_btc_address, :declines, :mark_decline,
+ :transactions
def initialize(
customer_id,
@@ 38,6 42,7 @@ class Customer
)
@plan = plan
@usage = CustomerUsage.new(customer_id)
+ @financials = CustomerFinancials.new(customer_id)
@customer_id = customer_id
@jid = jid
@balance = balance
@@ 61,14 66,6 @@ class Customer
)
end
- def payment_methods
- BRAINTREE
- .customer
- .find(@customer_id)
- .catch { OpenStruct.new(payment_methods: []) }
- .then(PaymentMethods.method(:for_braintree_customer))
- end
-
def unused_invites
promise = DB.query_defer(<<~SQL, [customer_id])
SELECT code FROM unused_invites WHERE creator_id=$1
@@ 105,23 102,6 @@ class Customer
sip_account.with_random_password.put
end
- def btc_addresses
- REDIS.smembers("jmp_customer_btc_addresses-#{customer_id}")
- end
-
- def add_btc_address
- REDIS.spopsadd([
- "jmp_available_btc_addresses",
- "jmp_customer_btc_addresses-#{customer_id}"
- ]).then do |addr|
- ELECTRUM.notify(
- addr,
- CONFIG[:electrum_notify_url].call(addr, customer_id)
- )
- addr
- end
- end
-
def admin?
CONFIG[:admins].include?(jid.to_s)
end
A lib/customer_finacials.rb => lib/customer_finacials.rb +74 -0
@@ 0,0 1,74 @@
+# frozen_string_literal: true
+
+class CustomerFinancials
+ def initialize(customer_id)
+ @customer_id = customer_id
+ end
+
+ def payment_methods
+ BRAINTREE
+ .customer
+ .find(@customer_id)
+ .catch { OpenStruct.new(payment_methods: []) }
+ .then(PaymentMethods.method(:for_braintree_customer))
+ end
+
+ def btc_addresses
+ REDIS.smembers("jmp_customer_btc_addresses-#{@customer_id}")
+ end
+
+ def add_btc_address
+ REDIS.spopsadd([
+ "jmp_available_btc_addresses",
+ "jmp_customer_btc_addresses-#{@customer_id}"
+ ]).then do |addr|
+ ELECTRUM.notify(
+ addr,
+ CONFIG[:electrum_notify_url].call(addr, @customer_id)
+ )
+ addr
+ end
+ end
+
+ def declines
+ REDIS.get("jmp_pay_decline-#{@customer_id}")
+ end
+
+ def mark_decline
+ REDIS.incr("jmp_pay_decline-#{@customer_id}").then do
+ REDIS.expire("jmp_pay_decline-#{@customer_id}", 60 * 60 * 24)
+ end
+ end
+
+ class TransactionInfo
+ value_semantics do
+ transaction_id String
+ created_at Time
+ amount BigDecimal
+ note String
+ end
+
+ def formatted_amount
+ "$%.4f" % amount
+ end
+ end
+
+ TRANSACTIONS_SQL = <<~SQL
+ SELECT
+ transaction_id,
+ created_at,
+ amount,
+ note
+ FROM transactions WHERE customer_id = $1;
+ SQL
+
+ def transactions
+ txns = DB.query_defer(TRANSACTIONS_SQL, [@customer_id])
+
+ txns.then do |rows|
+ rows.map { |row|
+ TransactionInfo.new(**row.transform_keys(&:to_sym))
+ }
+ end
+ end
+end
M lib/transaction.rb => lib/transaction.rb +2 -4
@@ 4,7 4,7 @@ require "bigdecimal"
class Transaction
def self.sale(customer, amount:, payment_method: nil)
- REDIS.get("jmp_pay_decline-#{customer.customer_id}").then do |declines|
+ customer.declines.then do |declines|
raise "too many declines" if declines.to_i >= 2
BRAINTREE.transaction.sale(
@@ 20,9 20,7 @@ class Transaction
def self.decline_guard(customer, response)
return if response.success?
- REDIS.incr("jmp_pay_decline-#{customer.customer_id}").then do
- REDIS.expire("jmp_pay_decline-#{customer.customer_id}", 60 * 60 * 24)
- end
+ customer.mark_decline
raise response.message
end
M test/test_alt_top_up_form.rb => test/test_alt_top_up_form.rb +3 -3
@@ 6,7 6,7 @@ require "customer"
class AltTopUpFormTest < Minitest::Test
def test_for
- Customer::REDIS.expect(
+ CustomerFinancials::REDIS.expect(
:smembers,
EMPromise.resolve([]),
["jmp_customer_btc_addresses-test"]
@@ 19,7 19,7 @@ class AltTopUpFormTest < Minitest::Test
em :test_for
def test_for_addresses
- Customer::REDIS.expect(
+ CustomerFinancials::REDIS.expect(
:smembers,
EMPromise.resolve(["testaddr"]),
["jmp_customer_btc_addresses-test"]
@@ 32,7 32,7 @@ class AltTopUpFormTest < Minitest::Test
em :test_for_addresses
def test_for_cad
- Customer::REDIS.expect(
+ CustomerFinancials::REDIS.expect(
:smembers,
EMPromise.resolve([]),
["jmp_customer_btc_addresses-test"]
M test/test_buy_account_credit_form.rb => test/test_buy_account_credit_form.rb +1 -1
@@ 17,7 17,7 @@ class BuyAccountCreditFormTest < Minitest::Test
def test_for
braintree_customer = Minitest::Mock.new
- Customer::BRAINTREE.expect(:customer, braintree_customer)
+ CustomerFinancials::BRAINTREE.expect(:customer, braintree_customer)
braintree_customer.expect(
:find,
EMPromise.resolve(OpenStruct.new(payment_methods: [])),
M test/test_customer.rb => test/test_customer.rb +8 -6
@@ 5,13 5,15 @@ require "customer"
Customer::BLATHER = Minitest::Mock.new
Customer::BRAINTREE = Minitest::Mock.new
-Customer::ELECTRUM = Minitest::Mock.new
Customer::REDIS = Minitest::Mock.new
Customer::DB = Minitest::Mock.new
Customer::IQ_MANAGER = Minitest::Mock.new
CustomerPlan::DB = Minitest::Mock.new
CustomerUsage::REDIS = Minitest::Mock.new
CustomerUsage::DB = Minitest::Mock.new
+CustomerFinancials::REDIS = Minitest::Mock.new
+CustomerFinancials::ELECTRUM = Minitest::Mock.new
+CustomerFinancials::BRAINTREE = Minitest::Mock.new
class CustomerTest < Minitest::Test
def test_bill_plan_activate
@@ 191,7 193,7 @@ class CustomerTest < Minitest::Test
em :test_sip_account_error
def test_btc_addresses
- Customer::REDIS.expect(
+ CustomerFinancials::REDIS.expect(
:smembers,
EMPromise.resolve(["testaddr"]),
["jmp_customer_btc_addresses-test"]
@@ 202,19 204,19 @@ class CustomerTest < Minitest::Test
em :test_btc_addresses
def test_add_btc_address
- Customer::REDIS.expect(
+ CustomerFinancials::REDIS.expect(
:spopsadd,
EMPromise.resolve("testaddr"),
[["jmp_available_btc_addresses", "jmp_customer_btc_addresses-test"]]
)
- Customer::ELECTRUM.expect(
+ CustomerFinancials::ELECTRUM.expect(
:notify,
EMPromise.resolve(nil),
["testaddr", "http://notify.example.com"]
)
assert_equal "testaddr", customer.add_btc_address.sync
- assert_mock Customer::REDIS
- assert_mock Customer::ELECTRUM
+ assert_mock CustomerFinancials::REDIS
+ assert_mock CustomerFinancials::ELECTRUM
end
em :test_add_btc_address
end
M test/test_low_balance.rb => test/test_low_balance.rb +2 -2
@@ 5,7 5,7 @@ require "low_balance"
ExpiringLock::REDIS = Minitest::Mock.new
CustomerPlan::REDIS = Minitest::Mock.new
-Customer::REDIS = Minitest::Mock.new
+CustomerFinancials::REDIS = Minitest::Mock.new
class LowBalanceTest < Minitest::Test
def test_for_locked
@@ 24,7 24,7 @@ class LowBalanceTest < Minitest::Test
EMPromise.resolve(0),
["jmp_customer_low_balance-test"]
)
- Customer::REDIS.expect(
+ CustomerFinancials::REDIS.expect(
:smembers,
EMPromise.resolve([]),
["jmp_customer_btc_addresses-test"]
M test/test_registration.rb => test/test_registration.rb +4 -4
@@ 254,7 254,7 @@ class RegistrationTest < Minitest::Test
end
class PaymentTest < Minitest::Test
- Customer::BRAINTREE = Minitest::Mock.new
+ CustomerFinancials::BRAINTREE = Minitest::Mock.new
def test_for_bitcoin
cust = Minitest::Mock.new(customer)
@@ 273,7 273,7 @@ class RegistrationTest < Minitest::Test
def test_for_credit_card
braintree_customer = Minitest::Mock.new
- Customer::BRAINTREE.expect(
+ CustomerFinancials::BRAINTREE.expect(
:customer,
braintree_customer
)
@@ 313,7 313,7 @@ class RegistrationTest < Minitest::Test
class BitcoinTest < Minitest::Test
Registration::Payment::Bitcoin::BTC_SELL_PRICES = Minitest::Mock.new
- Customer::REDIS = Minitest::Mock.new
+ CustomerFinancials::REDIS = Minitest::Mock.new
def setup
@customer = Minitest::Mock.new(
@@ 330,7 330,7 @@ class RegistrationTest < Minitest::Test
end
def test_write
- Customer::REDIS.expect(
+ CustomerFinancials::REDIS.expect(
:smembers,
EMPromise.resolve([]),
["jmp_customer_btc_addresses-test"]
M test/test_transaction.rb => test/test_transaction.rb +6 -4
@@ 18,17 18,17 @@ class TransactionTest < Minitest::Test
)
def test_sale_fails
- Transaction::REDIS.expect(
+ CustomerFinancials::REDIS.expect(
:get,
EMPromise.resolve("1"),
["jmp_pay_decline-test"]
)
- Transaction::REDIS.expect(
+ CustomerFinancials::REDIS.expect(
:incr,
EMPromise.resolve(nil),
["jmp_pay_decline-test"]
)
- Transaction::REDIS.expect(
+ CustomerFinancials::REDIS.expect(
:expire,
EMPromise.resolve(nil),
["jmp_pay_decline-test", 60 * 60 * 24]
@@ 49,11 49,12 @@ class TransactionTest < Minitest::Test
payment_method: OpenStruct.new(token: "token")
).sync
end
+ assert_mock CustomerFinancials::REDIS
end
em :test_sale_fails
def test_sale
- Transaction::REDIS.expect(
+ CustomerFinancials::REDIS.expect(
:get,
EMPromise.resolve("1"),
["jmp_pay_decline-test"]
@@ 81,6 82,7 @@ class TransactionTest < Minitest::Test
payment_method: OpenStruct.new(token: "token")
).sync
assert_kind_of Transaction, result
+ assert_mock CustomerFinancials::REDIS
end
em :test_sale