M config.dhall.sample => config.dhall.sample +4 -1
@@ 63,5 63,8 @@
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}"
+ "https://pay.jmp.chat/${jid}/credit_cards?customer_id=${customer_id}",
+ adr = "",
+ interac = "",
+ payable = ""
}
A lib/add_bitcoin_address.rb => lib/add_bitcoin_address.rb +50 -0
@@ 0,0 1,50 @@
+# frozen_string_literal: true
+
+class AddBitcoinAddress
+ def self.for(iq, alt_form, customer)
+ if alt_form.parse(iq.form)[:add_btc_address]
+ new(iq, customer)
+ else
+ DoNot.new(iq)
+ end
+ end
+
+ def initialize(iq, customer)
+ @reply = iq.reply
+ @reply.status = :completed
+ @customer = customer
+ end
+
+ def write
+ @customer.add_btc_address.then do |addr|
+ form.fields = [{
+ var: "btc_address",
+ type: "fixed",
+ label: "Bitcoin Address",
+ value: addr
+ }]
+ BLATHER << @reply
+ end
+ end
+
+protected
+
+ def form
+ form = @reply.form
+ form.type = :result
+ form.title = "New Bitcoin Address"
+ form.instructions = "Your new address has been created"
+ form
+ end
+
+ class DoNot
+ def initialize(iq)
+ @reply = iq.reply
+ @reply.status = :completed
+ end
+
+ def write
+ BLATHER << @reply
+ end
+ end
+end
A lib/alt_top_up_form.rb => lib/alt_top_up_form.rb +115 -0
@@ 0,0 1,115 @@
+# frozen_string_literal: true
+
+class AltTopUpForm
+ def self.for(customer)
+ customer.btc_addresses.then do |addrs|
+ AltTopUpForm.new(*[
+ (IS_CAD if customer.currency == :CAD),
+ (HasBitcoinAddresses.new(addrs) unless addrs.empty?),
+ AddBtcAddressField.for(addrs)
+ ].compact)
+ end
+ end
+
+ def initialize(*fields)
+ @fields = fields
+ end
+
+ def form
+ form = Blather::Stanza::X.new(:result)
+ form.type = :form
+ form.title = "Buy Account Credit"
+ form.instructions =
+ "Besides credit cards, we support payment by Bitcoin, postal mail, " \
+ "or in Canada by Interac eTransfer."
+
+ form.fields = fields.to_a
+ form
+ end
+
+ def parse(form)
+ {
+ add_btc_address: ["1", "true"].include?(
+ form.field("add_btc_address")&.value.to_s
+ )
+ }
+ end
+
+ MAILING_ADDRESS = {
+ var: "adr",
+ type: "fixed",
+ label: "Mailing Address",
+ description:
+ "Make payable to #{CONFIG[:payable]} and include your " \
+ "Jabber ID in the mailing somewhere.",
+ value: CONFIG[:adr]
+ }.freeze
+
+ def fields
+ Enumerator.new do |y|
+ y << MAILING_ADDRESS
+ @fields.each do |fs|
+ fs.each { |f| y << f }
+ end
+ end
+ end
+
+ IS_CAD = [
+ var: "adr",
+ type: "fixed",
+ label: "Interac eTransfer Address",
+ description: "Please include your Jabber ID in the note",
+ value: CONFIG[:interac]
+ ].freeze
+
+ class AddBtcAddressField
+ def self.for(addrs)
+ if addrs.empty?
+ AddNewBtcAddressField.new
+ else
+ new
+ end
+ end
+
+ def each
+ yield(
+ var: "add_btc_address",
+ label: label,
+ type: "boolean",
+ value: false
+ )
+ end
+
+ def label
+ "Or, create a new Bitcoin address?"
+ end
+
+ class AddNewBtcAddressField < AddBtcAddressField
+ def label
+ "You have no Bitcoin addresses, would you like to create one?"
+ end
+ end
+ end
+
+ class HasBitcoinAddresses
+ def initialize(addrs)
+ @addrs = addrs
+ end
+
+ DESCRIPTION =
+ "You can make a Bitcoin payment of any amount to any " \
+ "of these addresses and it will be credited to your " \
+ "account at the Canadian Bitcoins exchange rate within 5 " \
+ "minutes of your transaction reaching 3 confirmations."
+
+ def each
+ yield(
+ var: "btc_address",
+ type: "fixed",
+ label: "Bitcoin Addresses",
+ description: DESCRIPTION,
+ value: @addrs
+ )
+ end
+ end
+end
M lib/command_list.rb => lib/command_list.rb +5 -0
@@ 24,6 24,7 @@ class CommandList
]).then do |(fwd, payment_methods)|
Registered.new(*[
(HAS_CREDIT_CARD unless payment_methods.empty?),
+ (HAS_CURRENCY if customer.currency),
(HAS_FORWARDING if fwd)
].compact)
end
@@ 52,6 53,10 @@ class CommandList
end
end
+ HAS_CURRENCY = [
+ node: "alt top up",
+ name: "Buy Account Credit by Bitcoin, Mail, or Interac eTransfer"
+ ].freeze
HAS_FORWARDING = [
node: "record-voicemail-greeting",
M sgx_jmp.rb => sgx_jmp.rb +24 -0
@@ 24,6 24,8 @@ singleton_class.class_eval do
Blather::DSL.append_features(self)
end
+require_relative "lib/alt_top_up_form"
+require_relative "lib/add_bitcoin_address"
require_relative "lib/backend_sgx"
require_relative "lib/bandwidth_tn_order"
require_relative "lib/btc_sell_prices"
@@ 370,6 372,28 @@ command :execute?, node: "top up", sessionid: nil do |iq|
}.catch { |e| panic(e, sentry_hub) }
end
+command :execute?, node: "alt top up", sessionid: nil do |iq|
+ sentry_hub = new_sentry_hub(iq, name: iq.node)
+ reply = iq.reply
+ reply.status = :executing
+ reply.allowed_actions = [:complete]
+
+ Customer.for_jid(iq.from.stripped).then { |customer|
+ sentry_hub.current_scope.set_user(
+ id: customer.customer_id,
+ jid: iq.from.stripped.to_s
+ )
+
+ EMPromise.all([AltTopUpForm.for(customer), customer])
+ }.then { |(alt_form, customer)|
+ reply.command << alt_form.form
+
+ COMMAND_MANAGER.write(reply).then do |iq2|
+ AddBitcoinAddress.for(iq2, alt_form, customer).write
+ end
+ }.catch { |e| panic(e, sentry_hub) }
+end
+
command :execute?, node: "reset sip account", sessionid: nil do |iq|
sentry_hub = new_sentry_hub(iq, name: iq.node)
Customer.for_jid(iq.from.stripped).then { |customer|
A test/test_add_bitcoin_address.rb => test/test_add_bitcoin_address.rb +44 -0
@@ 0,0 1,44 @@
+# frozen_string_literal: true
+
+require "test_helper"
+require "alt_top_up_form"
+require "add_bitcoin_address"
+
+class AddBitcoinAddressTest < Minitest::Test
+ def test_for
+ iq = Blather::Stanza::Iq::Command.new
+ AddBitcoinAddress.for(iq, AltTopUpForm.new, Customer.new("test"))
+ end
+
+ def test_for_add_bitcoin
+ iq = Blather::Stanza::Iq::Command.new
+ iq.form.fields = [{ var: "add_btc_address", value: "true" }]
+ AddBitcoinAddress.for(iq, AltTopUpForm.new, Customer.new("test"))
+ end
+
+ def test_write
+ customer = Minitest::Mock.new
+ customer.expect(:add_btc_address, EMPromise.resolve("newaddress"))
+ iq = Blather::Stanza::Iq::Command.new
+ AddBitcoinAddress.new(iq, customer).write.sync
+ assert_mock customer
+ end
+ em :test_write
+
+ class DoNotTest < Minitest::Test
+ AddBitcoinAddress::DoNot::BLATHER = Minitest::Mock.new
+
+ def test_write
+ AddBitcoinAddress::DoNot::BLATHER.expect(
+ :<<,
+ EMPromise.resolve(nil)
+ ) do |stanza|
+ assert_equal :completed, stanza.status
+ end
+ iq = Blather::Stanza::Iq::Command.new
+ AddBitcoinAddress::DoNot.new(iq).write.sync
+ assert_mock AddBitcoinAddress::DoNot::BLATHER
+ end
+ em :test_write
+ end
+end
A test/test_alt_top_up_form.rb => test/test_alt_top_up_form.rb +91 -0
@@ 0,0 1,91 @@
+# frozen_string_literal: true
+
+require "test_helper"
+require "alt_top_up_form"
+require "customer"
+
+class AltTopUpFormTest < Minitest::Test
+ def test_for
+ Customer::REDIS.expect(
+ :smembers,
+ EMPromise.resolve([]),
+ ["jmp_customer_btc_addresses-test"]
+ )
+ assert_kind_of(
+ AltTopUpForm,
+ AltTopUpForm.for(Customer.new("test")).sync
+ )
+ end
+ em :test_for
+
+ def test_for_addresses
+ Customer::REDIS.expect(
+ :smembers,
+ EMPromise.resolve(["testaddr"]),
+ ["jmp_customer_btc_addresses-test"]
+ )
+ assert_kind_of(
+ AltTopUpForm,
+ AltTopUpForm.for(Customer.new("test")).sync
+ )
+ end
+ em :test_for_addresses
+
+ def test_for_cad
+ Customer::REDIS.expect(
+ :smembers,
+ EMPromise.resolve([]),
+ ["jmp_customer_btc_addresses-test"]
+ )
+ assert_kind_of(
+ AltTopUpForm,
+ AltTopUpForm.for(Customer.new("test", plan_name: "test_cad")).sync
+ )
+ end
+ em :test_for_cad
+
+ def test_form_addrs
+ assert_kind_of(
+ Blather::Stanza::X,
+ AltTopUpForm.new(AltTopUpForm::AddBtcAddressField.new).form
+ )
+ end
+
+ def test_form_new_addrs
+ assert_kind_of(
+ Blather::Stanza::X,
+ AltTopUpForm.new(
+ AltTopUpForm::AddBtcAddressField::AddNewBtcAddressField.new
+ ).form
+ )
+ end
+
+ def test_parse_true
+ iq_form = Blather::Stanza::X.new
+ iq_form.fields = [
+ { var: "add_btc_address", value: "true" }
+ ]
+ assert AltTopUpForm.new.parse(iq_form)[:add_btc_address]
+ end
+
+ def test_parse_1
+ iq_form = Blather::Stanza::X.new
+ iq_form.fields = [
+ { var: "add_btc_address", value: "1" }
+ ]
+ assert AltTopUpForm.new.parse(iq_form)[:add_btc_address]
+ end
+
+ def test_parse_false
+ iq_form = Blather::Stanza::X.new
+ iq_form.fields = [
+ { var: "add_btc_address", value: "false" }
+ ]
+ refute AltTopUpForm.new.parse(iq_form)[:add_btc_address]
+ end
+
+ def test_parse_not_presend
+ iq_form = Blather::Stanza::X.new
+ refute AltTopUpForm.new.parse(iq_form)[:add_btc_address]
+ end
+end
M test/test_command_list.rb => test/test_command_list.rb +9 -8
@@ 92,24 92,25 @@ class CommandListTest < Minitest::Test
end
em :test_for_registered_with_credit_card
- def test_for_registered_with_forwarding_and_billing
+ def test_for_registered_with_currency
CommandList::REDIS.expect(
:get,
- EMPromise.resolve("tel:1"),
+ EMPromise.resolve(nil),
["catapult_fwd-1"]
)
CommandList::Customer.expect(
:for_jid,
EMPromise.resolve(OpenStruct.new(
registered?: OpenStruct.new(phone: "1"),
- plan_name: "test",
- payment_methods: EMPromise.resolve([:boop])
+ currency: :USD
)),
["registered"]
)
- result = CommandList.for("registered").sync
- assert_kind_of CommandList::HasForwarding, result
- assert_kind_of CommandList::HasBilling, result
+
+ assert_equal(
+ CommandList::HAS_CURRENCY,
+ CommandList::HAS_CURRENCY & CommandList.for("registered").sync.to_a
+ )
end
- em :test_for_registered_with_forwarding_and_billing
+ em :test_for_registered_with_currency
end
M test/test_helper.rb => test/test_helper.rb +5 -0
@@ 62,6 62,11 @@ CONFIG = {
{
name: "test_bad_currency",
currency: :BAD
+ },
+ {
+ name: "test_cad",
+ currency: :CAD,
+ monthly_price: 1000
}
],
braintree: {