@@ 9,11 9,14 @@ require "em-hiredis"
require "em_promise"
require "time-hash"
-CONFIG = Dhall::Coder.load(ARGV[0])
+CONFIG =
+ Dhall::Coder
+ .new(safe: Dhall::Coder::JSON_LIKE + [Symbol])
+ .load(ARGV[0], transform_keys: ->(k) { k&.to_sym })
# Braintree is not async, so wrap in EM.defer for now
class AsyncBraintree
- def initialize(environment:, merchant_id:, public_key:, private_key:)
+ def initialize(environment:, merchant_id:, public_key:, private_key:, **)
@gateway = Braintree::Gateway.new(
environment: environment,
merchant_id: merchant_id,
@@ 50,7 53,7 @@ class AsyncBraintree
end
end
-BRAINTREE = AsyncBraintree.new(**CONFIG["braintree"].transform_keys(&:to_sym))
+BRAINTREE = AsyncBraintree.new(**CONFIG[:braintree])
def node(name, parent, ns: nil)
Niceogiri::XML::Node.new(
@@ 94,7 97,7 @@ end
def proxy_jid(jid)
Blather::JID.new(
escape_jid(jid.stripped),
- CONFIG["component"]["jid"],
+ CONFIG[:component][:jid],
jid.resource
)
end
@@ 162,18 165,18 @@ when_ready do
DB.type_map_for_queries = PG::BasicTypeMapForQueries.new(DB)
EM.add_periodic_timer(3600) do
- ping = Blather::Stanza::Iq::Ping.new(:get, CONFIG["server"]["host"])
- ping.from = CONFIG["component"]["jid"]
+ ping = Blather::Stanza::Iq::Ping.new(:get, CONFIG[:server][:host])
+ ping.from = CONFIG[:component][:jid]
self << ping
end
end
# workqueue_count MUST be 0 or else Blather uses threads!
setup(
- CONFIG["component"]["jid"],
- CONFIG["component"]["secret"],
- CONFIG["server"]["host"],
- CONFIG["server"]["port"],
+ CONFIG[:component][:jid],
+ CONFIG[:component][:secret],
+ CONFIG[:server][:host],
+ CONFIG[:server][:port],
nil,
nil,
workqueue_count: 0
@@ 186,7 189,7 @@ end
ibr :get? do |iq|
fwd = iq.dup
fwd.from = proxy_jid(iq.from)
- fwd.to = Blather::JID.new(nil, CONFIG["sgx"], iq.to.resource)
+ fwd.to = Blather::JID.new(nil, CONFIG[:sgx], iq.to.resource)
fwd.id = "JMPGET%#{iq.id}"
self << fwd
end
@@ 205,7 208,7 @@ ibr :result? do |iq|
reply.id = iq.id.sub(/JMP[GS]ET%/, "")
reply.from = Blather::JID.new(
nil,
- CONFIG["component"]["jid"],
+ CONFIG[:component][:jid],
iq.from.resource
)
reply.to = unproxy_jid(iq.to)
@@ 217,7 220,7 @@ ibr :error? do |iq|
reply.id = iq.id.sub(/JMP[GS]ET%/, "")
reply.from = Blather::JID.new(
nil,
- CONFIG["component"]["jid"],
+ CONFIG[:component][:jid],
iq.from.resource
)
reply.to = unproxy_jid(iq.to)
@@ 226,11 229,11 @@ end
ibr :set? do |iq|
fwd = iq.dup
- CONFIG["creds"].each do |k, v|
+ CONFIG[:creds].each do |k, v|
fwd.public_send("#{k}=", v)
end
fwd.from = proxy_jid(iq.from)
- fwd.to = Blather::JID.new(nil, CONFIG["sgx"], iq.to.resource)
+ fwd.to = Blather::JID.new(nil, CONFIG[:sgx], iq.to.resource)
fwd.id = "JMPSET%#{iq.id}"
self << fwd
end
@@ 289,6 292,7 @@ disco_items node: "http://jabber.org/protocol/commands" do |iq|
reply = iq.reply
reply.items = [
# TODO: don't show this item if no braintree methods available
+ # TODO: don't show this item if no plan for this customer
Blather::Stanza::DiscoItems::Item.new(
iq.to,
"buy-credit",
@@ 310,16 314,23 @@ command :execute?, node: "buy-credit", sessionid: nil do |iq|
EMPromise.all([
DB.query_defer(
- "SELECT balance FROM balances WHERE customer_id=$1 LIMIT 1",
+ "SELECT COALESCE(balance,0) AS balance, plan_name FROM " \
+ "balances LEFT JOIN customer_plans USING (customer_id) " \
+ "WHERE customer_id=$1 LIMIT 1",
[customer_id]
).then do |rows|
- rows.first&.dig("balance") || BigDecimal.new(0)
+ rows.first || { "balance" => BigDecimal.new(0) }
end,
BRAINTREE.customer.find(customer_id).payment_methods
])
- }.then { |(balance, payment_methods)|
+ }.then { |(row, payment_methods)|
raise "No payment methods available" if payment_methods.empty?
+ plan = CONFIG[:plans].find { |p| p[:name] == row["plan_name"] }
+ raise "No plan for this customer" unless plan
+ merchant_account = CONFIG[:braintree][:merchant_accounts][plan[:currency]]
+ raise "No merchant account for this currency" unless merchant_account
+
default_payment_method = payment_methods.index(&:default?)
form = reply.form
@@ 328,7 339,7 @@ command :execute?, node: "buy-credit", sessionid: nil do |iq|
form.fields = [
{
type: "fixed",
- value: "Current balance: $#{'%.2f' % balance}"
+ value: "Current balance: $#{'%.2f' % row['balance']}"
},
if payment_methods.length > 1
{
@@ 356,9 367,10 @@ command :execute?, node: "buy-credit", sessionid: nil do |iq|
EMPromise.all([
payment_methods,
+ merchant_account,
command_reply_and_promise(reply)
])
- }.then { |(payment_methods, iq2)|
+ }.then { |(payment_methods, merchant_account, iq2)|
iq = iq2 # This allows the catch to use it also
payment_method = payment_methods.fetch(
iq.form.field("payment_method")&.value.to_i
@@ 366,6 378,7 @@ command :execute?, node: "buy-credit", sessionid: nil do |iq|
BRAINTREE.transaction.sale(
amount: iq.form.field("amount").value.to_s,
payment_method_token: payment_method.token,
+ merchant_account_id: merchant_account,
options: { submit_for_settlement: true }
)
}.then { |braintree_response|