M config.dhall.sample => config.dhall.sample +8 -4
@@ 8,11 8,12 @@
port = 5347
},
sgx = "component2.localhost",
- creds = toMap {
- nick = "userid",
- username = "token",
- password = "secret"
+ creds = {
+ account = "00000",
+ username = "dashboard user",
+ password = "dashboard password"
},
+ bandwidth_site = "",
braintree = {
environment = "sandbox",
merchant_id = "",
@@ 24,6 25,9 @@
}
},
plans = ./plans.dhall
+ electrum = ./electrum.dhall,
+ oxr_app_id = "",
+ activation_amount = 15,
credit_card_url = \(jid: Text) -> \(customer_id: Text) ->
"https://pay.jmp.chat/${jid}/credit_cards?customer_id=${customer_id}"
}
M lib/registration.rb => lib/registration.rb +44 -5
@@ 65,7 65,7 @@ class Registration
},
{
value: "credit_card",
- label: "Credit Card"
+ label: "Credit Card ($#{CONFIG[:activation_amount]})"
},
{
value: "code",
@@ 173,8 173,8 @@ class Registration
def self.for(iq, customer, tel)
customer.payment_methods.then do |payment_methods|
- if payment_methods.default_payment_method
- Activate.new(iq, customer, tel)
+ if (method = payment_methods.default_payment_method)
+ Activate.new(iq, customer, method, tel)
else
new(iq, customer, tel)
end
@@ 210,10 210,49 @@ class Registration
end
class Activate
- def initialize(_iq, _customer, _tel)
- raise "TODO"
+ def initialize(iq, customer, payment_method, tel)
+ @iq = iq
+ @customer = customer
+ @payment_method = payment_method
+ @tel = tel
+ end
+
+ def write
+ Transaction.sale(
+ @customer.merchant_account,
+ @payment_method,
+ CONFIG[:activation_amount]
+ ).then(&:insert).then {
+ @customer.bill_plan
+ }.then do
+ Finish.new(@iq, @customer, @tel).write
+ end
end
end
end
end
+
+ class Finish
+ def initialize(iq, customer, tel)
+ @reply = iq.reply
+ @reply.status = :completed
+ @reply.note_type = :info
+ @reply.note_text = "Your JMP account has been activated as #{tel}"
+ @customer = customer
+ @tel = tel
+ end
+
+ def write
+ BandwidthTNOrder.create(@tel).then(&:poll).then(
+ ->(_) { @customer.register!(@tel).then { BLATHER << @reply } },
+ lambda do |_|
+ @reply.note_type = :error
+ @reply.note_text =
+ "The JMP number #{@tel} is no longer available, " \
+ "please visit https://jmp.chat and choose another."
+ BLATHER << @reply
+ end
+ )
+ end
+ end
end
M sgx_jmp.rb => sgx_jmp.rb +16 -5
@@ 8,12 8,20 @@ require "braintree"
require "dhall"
require "em-hiredis"
require "em_promise"
+require "ruby-bandwidth-iris"
+
+CONFIG =
+ Dhall::Coder
+ .new(safe: Dhall::Coder::JSON_LIKE + [Symbol, Proc])
+ .load(ARGV[0], transform_keys: ->(k) { k&.to_sym })
singleton_class.class_eval do
include Blather::DSL
Blather::DSL.append_features(self)
end
+require_relative "lib/backend_sgx"
+require_relative "lib/bandwidth_tn_order"
require_relative "lib/btc_sell_prices"
require_relative "lib/buy_account_credit_form"
require_relative "lib/customer"
@@ 24,13 32,15 @@ require_relative "lib/registration"
require_relative "lib/transaction"
require_relative "lib/web_register_manager"
-CONFIG =
- Dhall::Coder
- .new(safe: Dhall::Coder::JSON_LIKE + [Symbol, Proc])
- .load(ARGV[0], transform_keys: ->(k) { k&.to_sym })
-
ELECTRUM = Electrum.new(**CONFIG[:electrum])
+Faraday.default_adapter = :em_synchrony
+BandwidthIris::Client.global_options = {
+ account_id: CONFIG[:creds][:account],
+ username: CONFIG[:creds][:username],
+ password: CONFIG[:creds][:password]
+}
+
# Braintree is not async, so wrap in EM.defer for now
class AsyncBraintree
def initialize(environment:, merchant_id:, public_key:, private_key:, **)
@@ 67,6 77,7 @@ class AsyncBraintree
end
BRAINTREE = AsyncBraintree.new(**CONFIG[:braintree])
+BACKEND_SGX = BackendSgx.new
def panic(e)
m = e.respond_to?(:message) ? e.message : e
M test/test_helper.rb => test/test_helper.rb +11 -1
@@ 32,16 32,24 @@ rescue LoadError
nil
end
+require "backend_sgx"
+
CONFIG = {
sgx: "sgx",
component: {
jid: "component"
},
+ creds: {
+ account: "test_bw_account",
+ username: "test_bw_user",
+ password: "test_bw_password"
+ },
activation_amount: 1,
plans: [
{
name: "test_usd",
- currency: :USD
+ currency: :USD,
+ monthly_price: 1000
},
{
name: "test_bad_currency",
@@ 56,6 64,8 @@ CONFIG = {
credit_card_url: ->(*) { "http://creditcard.example.com" }
}.freeze
+BACKEND_SGX = Minitest::Mock.new(BackendSgx.new)
+
BLATHER = Class.new {
def <<(*); end
}.new.freeze
M test/test_registration.rb => test/test_registration.rb +161 -12
@@ 4,8 4,6 @@ require "test_helper"
require "registration"
class RegistrationTest < Minitest::Test
- Customer::IQ_MANAGER = Minitest::Mock.new
-
def test_for_activated
skip "Registration#for activated not implemented yet"
iq = Blather::Stanza::Iq::Command.new
@@ 14,10 12,10 @@ class RegistrationTest < Minitest::Test
em :test_for_activated
def test_for_not_activated_with_customer_id
- Customer::IQ_MANAGER.expect(
- :write,
+ BACKEND_SGX.expect(
+ :registered?,
EMPromise.resolve(nil),
- [Blather::Stanza::Iq]
+ ["test"]
)
web_manager = WebRegisterManager.new
web_manager["test@example.com"] = "+15555550000"
@@ 128,12 126,9 @@ class RegistrationTest < Minitest::Test
EMPromise.resolve("testaddr")
)
iq = Blather::Stanza::Iq::Command.new
- iq.form.fields = [
- { var: "plan_name", value: "test_usd" }
- ]
@bitcoin = Registration::Payment::Bitcoin.new(
iq,
- Customer.new("test"),
+ Customer.new("test", plan_name: "test_usd"),
"+15555550000"
)
end
@@ 169,15 164,32 @@ class RegistrationTest < Minitest::Test
class CreditCardTest < Minitest::Test
def setup
- iq = Blather::Stanza::Iq::Command.new
- iq.from = "test@example.com"
+ @iq = Blather::Stanza::Iq::Command.new
+ @iq.from = "test@example.com"
@credit_card = Registration::Payment::CreditCard.new(
- iq,
+ @iq,
Customer.new("test"),
"+15555550000"
)
end
+ def test_for
+ customer = Minitest::Mock.new(Customer.new("test"))
+ customer.expect(
+ :payment_methods,
+ EMPromise.resolve(OpenStruct.new(default_payment_method: :test))
+ )
+ assert_kind_of(
+ Registration::Payment::CreditCard::Activate,
+ Registration::Payment::CreditCard.for(
+ @iq,
+ customer,
+ "+15555550000"
+ ).sync
+ )
+ end
+ em :test_for
+
def test_reply
assert_equal [:execute, :next], @credit_card.reply.allowed_actions
assert_equal(
@@ 187,5 199,142 @@ class RegistrationTest < Minitest::Test
)
end
end
+
+ class ActivateTest < Minitest::Test
+ Registration::Payment::CreditCard::Activate::Finish =
+ Minitest::Mock.new
+ Registration::Payment::CreditCard::Activate::Transaction =
+ Minitest::Mock.new
+
+ def test_write
+ transaction = PromiseMock.new
+ transaction.expect(
+ :insert,
+ EMPromise.resolve(nil)
+ )
+ Registration::Payment::CreditCard::Activate::Transaction.expect(
+ :sale,
+ transaction,
+ [
+ "merchant_usd",
+ :test_default_method,
+ CONFIG[:activation_amount]
+ ]
+ )
+ iq = Blather::Stanza::Iq::Command.new
+ customer = Minitest::Mock.new(
+ Customer.new("test", plan_name: "test_usd")
+ )
+ customer.expect(:bill_plan, nil)
+ Registration::Payment::CreditCard::Activate::Finish.expect(
+ :new,
+ OpenStruct.new(write: nil),
+ [Blather::Stanza::Iq, customer, "+15555550000"]
+ )
+ Registration::Payment::CreditCard::Activate.new(
+ iq,
+ customer,
+ :test_default_method,
+ "+15555550000"
+ ).write.sync
+ Registration::Payment::CreditCard::Activate::Transaction.verify
+ transaction.verify
+ customer.verify
+ end
+ em :test_write
+ end
+ end
+
+ class FinishTest < Minitest::Test
+ Registration::Finish::BLATHER = Minitest::Mock.new
+
+ def setup
+ @finish = Registration::Finish.new(
+ Blather::Stanza::Iq::Command.new,
+ Customer.new("test"),
+ "+15555550000"
+ )
+ end
+
+ def test_write
+ create_order = stub_request(
+ :post,
+ "https://dashboard.bandwidth.com/v1.0/accounts//orders"
+ ).to_return(status: 201, body: <<~RESPONSE)
+ <OrderResponse>
+ <Order>
+ <id>test_order</id>
+ </Order>
+ </OrderResponse>
+ RESPONSE
+ stub_request(
+ :get,
+ "https://dashboard.bandwidth.com/v1.0/accounts//orders/test_order"
+ ).to_return(status: 201, body: <<~RESPONSE)
+ <OrderResponse>
+ <OrderStatus>COMPLETE</OrderStatus>
+ </OrderResponse>
+ RESPONSE
+ BACKEND_SGX.expect(
+ :register!,
+ EMPromise.resolve(OpenStruct.new(error?: false)),
+ ["test", "+15555550000"]
+ )
+ Registration::Finish::BLATHER.expect(
+ :<<,
+ nil,
+ [Matching.new do |reply|
+ assert_equal :completed, reply.status
+ assert_equal :info, reply.note_type
+ assert_equal(
+ "Your JMP account has been activated as +15555550000",
+ reply.note.content
+ )
+ end]
+ )
+ @finish.write.sync
+ assert_requested create_order
+ BACKEND_SGX.verify
+ Registration::Finish::BLATHER.verify
+ end
+ em :test_write
+
+ def test_write_tn_fail
+ create_order = stub_request(
+ :post,
+ "https://dashboard.bandwidth.com/v1.0/accounts//orders"
+ ).to_return(status: 201, body: <<~RESPONSE)
+ <OrderResponse>
+ <Order>
+ <id>test_order</id>
+ </Order>
+ </OrderResponse>
+ RESPONSE
+ stub_request(
+ :get,
+ "https://dashboard.bandwidth.com/v1.0/accounts//orders/test_order"
+ ).to_return(status: 201, body: <<~RESPONSE)
+ <OrderResponse>
+ <OrderStatus>FAILED</OrderStatus>
+ </OrderResponse>
+ RESPONSE
+ Registration::Finish::BLATHER.expect(
+ :<<,
+ nil,
+ [Matching.new do |reply|
+ assert_equal :completed, reply.status
+ assert_equal :error, reply.note_type
+ assert_equal(
+ "The JMP number +15555550000 is no longer available, " \
+ "please visit https://jmp.chat and choose another.",
+ reply.note.content
+ )
+ end]
+ )
+ @finish.write.sync
+ assert_requested create_order
+ Registration::Finish::BLATHER.verify
+ end
+ em :test_write_tn_fail
end
end