straight-server 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,191 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe StraightServer::Gateway do
4
+
5
+ before(:each) do
6
+ @gateway = StraightServer::GatewayOnConfig.find_by_id(1)
7
+ @order_mock = double("order mock")
8
+ [:id, :gateway=, :save, :to_h, :id=].each { |m| allow(@order_mock).to receive(m) }
9
+ @order_for_keychain_id_args = { amount: 1, keychain_id: 1, currency: nil, btc_denomination: nil }
10
+ end
11
+
12
+ it "checks for signature when creating a new order" do
13
+ @gateway.last_keychain_id = 0
14
+ expect( -> { @gateway.create_order(amount: 1, signature: 'invalid', id: 1) }).to raise_exception(StraightServer::GatewayModule::InvalidSignature)
15
+ expect(@gateway).to receive(:order_for_keychain_id).with(@order_for_keychain_id_args).once.and_return(@order_mock)
16
+ @gateway.create_order(amount: 1, signature: hmac_sha1(1, 'secret'), id: 1)
17
+ end
18
+
19
+ it "checks md5 signature only if that setting is set ON for a particular gateway" do
20
+ gateway1 = StraightServer::GatewayOnConfig.find_by_id(1)
21
+ gateway2 = StraightServer::GatewayOnConfig.find_by_id(2)
22
+ expect(gateway2).to receive(:order_for_keychain_id).with(@order_for_keychain_id_args).once.and_return(@order_mock)
23
+ expect( -> { gateway1.create_order(amount: 1, signature: 'invalid') }).to raise_exception
24
+ expect( -> { gateway2.create_order(amount: 1, signature: 'invalid') }).not_to raise_exception()
25
+ end
26
+
27
+ it "doesn't allow nil or empty order id if signature checks are enabled" do
28
+ expect( -> { @gateway.create_order(amount: 1, signature: 'invalid', id: nil) }).to raise_exception(StraightServer::GatewayModule::InvalidOrderId)
29
+ expect( -> { @gateway.create_order(amount: 1, signature: 'invalid', id: '') }).to raise_exception(StraightServer::GatewayModule::InvalidOrderId)
30
+ end
31
+
32
+ it "sets order amount in satoshis calculated from another currency" do
33
+ @gateway = StraightServer::GatewayOnConfig.find_by_id(2)
34
+ allow(@gateway).to receive(:address_for_keychain_id).and_return('address')
35
+ allow(@gateway.exchange_rate_adapters.first).to receive(:rate_for).and_return(450.5412)
36
+ expect(@gateway.create_order(amount: 2252.706, currency: 'USD').amount).to eq(500000000)
37
+ end
38
+
39
+ context "callback url" do
40
+
41
+ before(:each) do
42
+ @gateway = StraightServer::GatewayOnConfig.find_by_id(2) # Gateway 2 doesn't require signatures
43
+ @response_mock = double("http response mock")
44
+ expect(@response_mock).to receive(:body).once.and_return('body')
45
+ @order = create(:order)
46
+ allow(@order).to receive(:status).and_return(1)
47
+ allow(@order).to receive(:tid).and_return('tid1')
48
+ end
49
+
50
+ it "sends a request to the callback_url" do
51
+ expect(@response_mock).to receive(:code).twice.and_return("200")
52
+ expect(Net::HTTP).to receive(:get_response).and_return(@response_mock)
53
+ @gateway.order_status_changed(@order)
54
+ end
55
+
56
+ it "keeps sending request according to the callback schedule if there's an error" do
57
+ expect(@response_mock).to receive(:code).twice.and_return("404")
58
+ expect(@gateway).to receive(:sleep).exactly(10).times
59
+ expect(Net::HTTP).to receive(:get_response).exactly(11).times.and_return(@response_mock)
60
+ expect(URI).to receive(:parse).with('http://localhost:3001/payment-callback?' + @order.to_http_params).exactly(11).times
61
+ @gateway.order_status_changed(@order)
62
+ end
63
+
64
+ it "signs the callback if gateway has a secret" do
65
+ @gateway = StraightServer::GatewayOnConfig.find_by_id(1) # Gateway 1 requires signatures
66
+ expect(@response_mock).to receive(:code).twice.and_return("200")
67
+ expect(URI).to receive(:parse).with('http://localhost:3000/payment-callback?' + @order.to_http_params + "&signature=#{hmac_sha1(hmac_sha1(@order.id, 'secret'), 'secret')}")
68
+ expect(Net::HTTP).to receive(:get_response).and_return(@response_mock)
69
+ @gateway.order_status_changed(@order)
70
+ end
71
+
72
+ it "receives random data in :data params and sends it back in a callback request" do
73
+ @order.data = 'some random data'
74
+ expect(@gateway).to receive(:order_for_keychain_id).with(@order_for_keychain_id_args).once.and_return(@order)
75
+ @gateway.create_order(amount: 1, data: 'some random data')
76
+ expect(@response_mock).to receive(:code).twice.and_return("200")
77
+ expect(Net::HTTP).to receive(:get_response).and_return(@response_mock)
78
+ expect(URI).to receive(:parse).with('http://localhost:3001/payment-callback?' + @order.to_http_params + "&data=#{@order.data}")
79
+ @gateway.order_status_changed(@order)
80
+ end
81
+
82
+ it "saves callback url response in the order's record in DB" do
83
+ allow(@response_mock).to receive(:code).and_return("200")
84
+ allow(Net::HTTP).to receive(:get_response).and_return(@response_mock)
85
+ @gateway.order_status_changed(@order)
86
+ expect(@order.callback_response).to eq({code: "200", body: "body"})
87
+ end
88
+
89
+ end
90
+
91
+ describe "config based gateway" do
92
+
93
+ it "loads all the gateways from the config file and assigns correct attributes" do
94
+ gateway1 = StraightServer::GatewayOnConfig.find_by_id(1)
95
+ gateway2 = StraightServer::GatewayOnConfig.find_by_id(2)
96
+ expect(gateway1).to be_kind_of(StraightServer::GatewayOnConfig)
97
+ expect(gateway2).to be_kind_of(StraightServer::GatewayOnConfig)
98
+
99
+ expect(gateway1.pubkey).to eq('xpub-000')
100
+ expect(gateway1.confirmations_required).to eq(0)
101
+ expect(gateway1.order_class).to eq("StraightServer::Order")
102
+ expect(gateway1.name).to eq("default")
103
+
104
+ expect(gateway2.pubkey).to eq('xpub-001')
105
+ expect(gateway2.confirmations_required).to eq(0)
106
+ expect(gateway2.order_class).to eq("StraightServer::Order")
107
+ expect(gateway2.name).to eq("second_gateway")
108
+ end
109
+
110
+ it "saves and retrieves last_keychain_id from the file in the .straight dir" do
111
+ expect(File.read("#{ENV['HOME']}/.straight/default_last_keychain_id").to_i).to eq(0)
112
+ @gateway.increment_last_keychain_id!
113
+ expect(File.read("#{ENV['HOME']}/.straight/default_last_keychain_id").to_i).to eq(1)
114
+
115
+ expect(@gateway).to receive(:order_for_keychain_id).with(@order_for_keychain_id_args.merge({ keychain_id: 2})).once.and_return(@order_mock)
116
+ @gateway.create_order(amount: 1, signature: hmac_sha1(1, 'secret'), id: 1)
117
+ expect(File.read("#{ENV['HOME']}/.straight/default_last_keychain_id").to_i).to eq(2)
118
+ end
119
+
120
+ end
121
+
122
+ describe "db based gateway" do
123
+
124
+ before(:each) do
125
+ @gateway = StraightServer::GatewayOnDB.create(
126
+ confirmations_required: 0,
127
+ pubkey: 'xpub-000',
128
+ order_class: 'StraightServer::Order',
129
+ secret: 'secret',
130
+ name: 'default',
131
+ check_signature: true,
132
+ exchange_rate_adapter_names: ['Bitpay', 'Coinbase', 'Bitstamp']
133
+ )
134
+ end
135
+
136
+ it "saves and retrieves last_keychain_id from the db" do
137
+ expect(DB[:gateways][:name => 'default'][:last_keychain_id]).to eq(0)
138
+ @gateway.increment_last_keychain_id!
139
+ expect(DB[:gateways][:name => 'default'][:last_keychain_id]).to eq(1)
140
+
141
+ expect(@gateway).to receive(:order_for_keychain_id).with(@order_for_keychain_id_args.merge({ keychain_id: 2})).once.and_return(@order_mock)
142
+ @gateway.create_order(amount: 1, signature: hmac_sha1(1, 'secret'), id: 1)
143
+ expect(DB[:gateways][:name => 'default'][:last_keychain_id]).to eq(2)
144
+ end
145
+
146
+ end
147
+
148
+ describe "handling websockets" do
149
+
150
+ before(:each) do
151
+ @gateway.instance_variable_set(:@websockets, {})
152
+ @ws = double("websocket mock")
153
+ allow(@ws).to receive(:on).with(:close)
154
+ allow(@order_mock).to receive(:id).and_return(1)
155
+ allow(@order_mock).to receive(:status).and_return(0)
156
+ end
157
+
158
+ it "adds a new websocket for the order" do
159
+ @gateway.add_websocket_for_order(@ws, @order_mock)
160
+ expect(@gateway.instance_variable_get(:@websockets)).to eq({ 1 => @ws})
161
+ end
162
+
163
+ it "sends a message to the websocket when status of the order is changed and closes the connection" do
164
+ allow(@gateway).to receive(:send_callback_http_request) # ignoring the callback which sends an callback_url request
165
+ expect(@order_mock).to receive(:to_json).and_return("order json info")
166
+ expect(@ws).to receive(:send).with("order json info")
167
+ expect(@ws).to receive(:close)
168
+ @gateway.add_websocket_for_order(@ws, @order_mock)
169
+ @gateway.order_status_changed(@order_mock)
170
+ end
171
+
172
+ it "doesn't allow to listen to orders with statuses other than 0 or 1" do
173
+ allow(@order_mock).to receive(:status).and_return(2)
174
+ expect( -> { @gateway.add_websocket_for_order(@ws, @order_mock) }).to raise_exception(StraightServer::Gateway::WebsocketForCompletedOrder)
175
+ end
176
+
177
+ it "doesn't allow to create a second websocket for the same order" do
178
+ allow(@order_mock).to receive(:status).and_return(0)
179
+ @gateway.add_websocket_for_order(@ws, @order_mock)
180
+ expect( -> { @gateway.add_websocket_for_order(@ws, @order_mock) }).to raise_exception(StraightServer::Gateway::WebsocketExists)
181
+ end
182
+
183
+ end
184
+
185
+ def hmac_sha1(key, secret)
186
+ h = HMAC::SHA1.new('secret')
187
+ h << key.to_s
188
+ h.hexdigest
189
+ end
190
+
191
+ end
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe StraightServer::Order do
4
+
5
+ before(:each) do
6
+ # clean the database
7
+ DB.run("DELETE FROM orders")
8
+ @gateway = double("Straight Gateway mock")
9
+ allow(@gateway).to receive(:id).and_return(1)
10
+ @order = create(:order, gateway_id: @gateway.id)
11
+ allow(@gateway).to receive(:fetch_transactions_for).with(anything).and_return([])
12
+ allow(@gateway).to receive(:order_status_changed).with(anything)
13
+ allow(StraightServer::Gateway).to receive(:find_by_id).and_return(@gateway)
14
+ end
15
+
16
+ it "prepares data as http params" do
17
+ allow(@order).to receive(:tid).and_return("tid1")
18
+ expect(@order.to_http_params).to eq("order_id=#{@order.id}&amount=10&status=#{@order.status}&address=#{@order.address}&tid=tid1")
19
+ end
20
+
21
+ describe "DB interaction" do
22
+
23
+ it "saves a new order into the database" do
24
+ expect(DB[:orders][:keychain_id => @order.id]).not_to be_nil
25
+ end
26
+
27
+ it "updates an existing order" do
28
+ expect(DB[:orders][:keychain_id => @order.id][:status]).to eq(0)
29
+ @order.status = 1
30
+ @order.save
31
+ expect(DB[:orders][:keychain_id => @order.id][:status]).to eq(1)
32
+ end
33
+
34
+ it "finds first order in the database by id" do
35
+ expect(StraightServer::Order.find(id: @order.id)).to equal_order(@order)
36
+ end
37
+
38
+ it "finds first order in the database by keychain_id" do
39
+ expect(StraightServer::Order.find(keychain_id: @order.keychain_id)).to equal_order(@order)
40
+ end
41
+
42
+ it "finds orders in the database by any conditions" do
43
+ order1 = create(:order, gateway_id: @gateway.id)
44
+ order2 = create(:order, gateway_id: @gateway.id)
45
+
46
+ expect(StraightServer::Order.where(keychain_id: order1.keychain_id).first).to equal_order(order1)
47
+ expect(StraightServer::Order.where(keychain_id: order2.keychain_id).first).to equal_order(order2)
48
+ expect(StraightServer::Order.where(keychain_id: order2.keychain_id+1).first).to be_nil
49
+
50
+ end
51
+
52
+ describe "with validations" do
53
+
54
+ it "doesn't save order if the order with the same id exists" do
55
+ order = create(:order, gateway_id: @gateway.id)
56
+ expect( -> { create(:order, id: order.id, gateway_id: @gateway.id) }).to raise_error()
57
+ end
58
+
59
+ it "doesn't save order if the order with the same address exists" do
60
+ order = create(:order, gateway_id: @gateway.id)
61
+ expect( -> { create(:order, address: order.address) }).to raise_error()
62
+ end
63
+
64
+ it "doesn't save order if the order with the same keychain_id and gateway_id exists" do
65
+ order = create(:order, gateway_id: @gateway.id)
66
+ expect( -> { create(:order, keychain_id: order.id, gateway_id: order.gateway_id+1) }).not_to raise_error()
67
+ expect( -> { create(:order, keychain_id: order.id, gateway_id: order.gateway_id) }).to raise_error()
68
+ end
69
+
70
+ it "doesn't save order if the amount is invalid" do
71
+ expect( -> { create(:order, amount: 0) }).to raise_error()
72
+ end
73
+
74
+ it "doesn't save order if gateway_id is invalid" do
75
+ expect( -> { create(:order, gateway_id: 0) }).to raise_error()
76
+ end
77
+
78
+ end
79
+
80
+ end
81
+
82
+ end
@@ -0,0 +1,113 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe StraightServer::OrdersController do
4
+
5
+ before(:each) do
6
+ DB.run("DELETE FROM orders")
7
+ @gateway = gateway = StraightServer::Gateway.find_by_id(2)
8
+ allow(gateway).to receive(:address_for_keychain_id).and_return("address#{gateway.last_keychain_id+1}")
9
+ allow(gateway).to receive(:fetch_transactions_for).with(anything).and_return([])
10
+ allow(gateway).to receive(:send_callback_http_request)
11
+ end
12
+
13
+ describe "create action" do
14
+
15
+ it "creates an order and renders its attrs in json" do
16
+ allow(StraightServer::Thread).to receive(:new) # ignore periodic status checks, we're not testing it here
17
+ send_request "POST", '/gateways/2/orders', amount: 10
18
+ expect(response).to render_json_with(status: 0, amount: 10, address: "address1", tid: nil, id: :anything)
19
+ end
20
+
21
+ it "renders 409 error when an order cannot be created due to some validation errors" do
22
+ send_request "POST", '/gateways/2/orders', amount: 0
23
+ expect(response[0]).to eq(409)
24
+ expect(response[2]).to eq("Invalid order: amount is invalid")
25
+ end
26
+
27
+ it "starts tracking the order status in a separate thread" do
28
+ order_mock = double("order mock")
29
+ expect(order_mock).to receive(:start_periodic_status_check)
30
+ allow(order_mock).to receive(:to_h).and_return({})
31
+ expect(@gateway).to receive(:create_order).and_return(order_mock)
32
+ send_request "POST", '/gateways/2/orders', amount: 10
33
+ end
34
+
35
+ end
36
+
37
+ describe "show action" do
38
+
39
+ before(:each) do
40
+ @order_mock = double('order mock')
41
+ allow(@order_mock).to receive(:status).and_return(2)
42
+ allow(@order_mock).to receive(:to_json).and_return("order json mock")
43
+ end
44
+
45
+ it "renders json info about an order if it is found" do
46
+ allow(@order_mock).to receive(:status_changed?).and_return(false)
47
+ expect(StraightServer::Order).to receive(:[]).with(1).and_return(@order_mock)
48
+ send_request "GET", '/gateways/2/orders/1'
49
+ expect(response).to eq([200, {}, "order json mock"])
50
+ end
51
+
52
+ it "saves an order if status is updated" do
53
+ allow(@order_mock).to receive(:status_changed?).and_return(true)
54
+ expect(@order_mock).to receive(:save)
55
+ expect(StraightServer::Order).to receive(:[]).with(1).and_return(@order_mock)
56
+ send_request "GET", '/gateways/2/orders/1'
57
+ expect(response).to eq([200, {}, "order json mock"])
58
+ end
59
+
60
+ it "renders 404 if order is not found" do
61
+ expect(StraightServer::Order).to receive(:[]).with(1).and_return(nil)
62
+ send_request "GET", '/gateways/2/orders/1'
63
+ expect(response).to eq([404, {}, "GET /gateways/2/orders/1 Not found"])
64
+ end
65
+
66
+ end
67
+
68
+ describe "websocket action" do
69
+
70
+ before(:each) do
71
+ @gateway.instance_variable_set(:@websockets, {})
72
+ @ws_mock = double("websocket mock")
73
+ @order_mock = double("order mock")
74
+ allow(@ws_mock).to receive(:rack_response).and_return("ws rack response")
75
+ [:id, :gateway=, :save, :to_h, :id=].each { |m| allow(@order_mock).to receive(m) }
76
+ allow(@ws_mock).to receive(:on)
77
+ allow(Faye::WebSocket).to receive(:new).and_return(@ws_mock)
78
+ end
79
+
80
+ it "returns a websocket connection" do
81
+ allow(@order_mock).to receive(:status).and_return(0)
82
+ allow(StraightServer::Order).to receive(:[]).with(1).and_return(@order_mock)
83
+ send_request "GET", '/gateways/2/orders/1/websocket'
84
+ expect(response).to eq("ws rack response")
85
+ end
86
+
87
+ it "returns 403 when socket already exists" do
88
+ allow(@order_mock).to receive(:status).and_return(0)
89
+ allow(StraightServer::Order).to receive(:[]).with(1).twice.and_return(@order_mock)
90
+ send_request "GET", '/gateways/2/orders/1/websocket'
91
+ send_request "GET", '/gateways/2/orders/1/websocket'
92
+ expect(response).to eq([403, {}, "Someone is already listening to that order"])
93
+ end
94
+
95
+ it "returns 403 when order has is completed (status > 1 )" do
96
+ allow(@order_mock).to receive(:status).and_return(2)
97
+ allow(StraightServer::Order).to receive(:[]).with(1).and_return(@order_mock)
98
+ send_request "GET", '/gateways/2/orders/1/websocket'
99
+ expect(response).to eq([403, {}, "You cannot listen to this order because it is completed (status > 1)"])
100
+ end
101
+
102
+ end
103
+
104
+ def send_request(method, path, params={})
105
+ env = Hashie::Mash.new({ 'REQUEST_METHOD' => method, 'REQUEST_PATH' => path, 'params' => params })
106
+ @controller = StraightServer::OrdersController.new(env)
107
+ end
108
+
109
+ def response
110
+ @controller.response
111
+ end
112
+
113
+ end
@@ -0,0 +1,77 @@
1
+ # !!! The order in which we require files here is very important.
2
+
3
+ # 1. First, load dependencies and connect to the Database
4
+ require 'sequel'
5
+ require 'straight'
6
+ require 'fileutils' # This is required to cleanup the test .straight dir
7
+ require 'hashie'
8
+
9
+ Sequel.extension :migration
10
+ DB = Sequel.sqlite
11
+
12
+ # 2. Then we can run migrations BEFORE we load actual models
13
+ Sequel::Migrator.run(DB, File.expand_path('../', File.dirname(__FILE__)) + '/db/migrations/')
14
+
15
+ # 3. Load config and initializer so that we can read our test config file located in
16
+ # spec/.straight/config.yml
17
+
18
+ # 3.1 This tells initializer where to read the config file from
19
+ ENV['HOME'] = File.expand_path(File.dirname(__FILE__))
20
+
21
+ # 3.2 Actually load the initializer
22
+ require_relative "../lib/straight-server/config"
23
+ require_relative "../lib/straight-server/initializer"
24
+ include StraightServer::Initializer
25
+
26
+ read_config_file
27
+
28
+ # 4. Load the rest of the files, including models, which are now ready
29
+ # to be used as intended and will follow all the previous configuration.
30
+ require_relative '../lib/straight-server/order'
31
+ require_relative '../lib/straight-server/gateway'
32
+ require_relative '../lib/straight-server/orders_controller'
33
+ require_relative '../lib/straight-server'
34
+
35
+ require_relative 'support/custom_matchers'
36
+
37
+ require "factory_girl"
38
+ require_relative "factories"
39
+
40
+ class StraightServer::Order
41
+ alias :save! :save
42
+ end
43
+
44
+ class StraightServer::Thread
45
+ def self.new(&block)
46
+ block.call
47
+ end
48
+ end
49
+
50
+ RSpec.configure do |config|
51
+
52
+ config.include FactoryGirl::Syntax::Methods
53
+
54
+ config.before(:suite) do
55
+ StraightServer.db_connection = DB #use a memory DB
56
+ end
57
+
58
+ config.before(:each) do
59
+ DB[:orders].delete
60
+ logger_mock = double("logger mock")
61
+ [:debug, :info, :warn, :fatal, :unknown, :blank_lines].each do |e|
62
+ allow(logger_mock).to receive(e)
63
+ end
64
+ StraightServer.logger = logger_mock
65
+ StraightServer::GatewayOnConfig.class_variable_get(:@@gateways).each do |g|
66
+ g.last_keychain_id = 0
67
+ g.save
68
+ end
69
+ end
70
+
71
+ config.after(:all) do
72
+ ["default_last_keychain_id", "second_gateway_last_keychain_id"].each do |f|
73
+ FileUtils.rm "#{ENV['HOME']}/.straight/#{f}" if File.exists?("#{ENV['HOME']}/.straight/#{f}")
74
+ end
75
+ end
76
+
77
+ end
@@ -0,0 +1,44 @@
1
+ RSpec::Matchers.define :equal_order do |expected|
2
+ match do |actual|
3
+ true
4
+ actual.address == expected.address &&
5
+ actual.status == expected.status &&
6
+ actual.keychain_id == expected.keychain_id &&
7
+ actual.amount == expected.amount &&
8
+ actual.gateway_id == expected.gateway_id &&
9
+ actual.id == expected.id
10
+ end
11
+
12
+ diffable
13
+ end
14
+
15
+ RSpec::Matchers.define :render_json_with do |hash|
16
+
17
+ match do |r|
18
+ json_response = JSON.parse(r[2])
19
+ check_one_dimensional_hash(hash, json_response)
20
+ end
21
+
22
+ def check_one_dimensional_hash(hash, json_response)
23
+ hash.each do |k,v|
24
+ if v == :anything
25
+ expect(json_response[k.to_s]).to_not be_nil
26
+ elsif v == nil
27
+ expect(json_response[k.to_s]).to be_nil
28
+ elsif v.kind_of?(Hash)
29
+ expect(json_response[k.to_s].kind_of?(Hash)).to be_truthy
30
+ check_one_dimensional_hash(v, json_response[k.to_s])
31
+ else
32
+ expect(json_response[k.to_s]).to eq(v)
33
+ end
34
+ end
35
+ end
36
+
37
+ failure_message do |actual|
38
+ "expected that it had:\n\n\t\t#{hash},\n\nbut instead it had:\n\n\t\t#{JSON.parse(actual[2])}"
39
+ end
40
+ failure_message_when_negated do |actual|
41
+ "expected that it wouldn't render #{hash.inspect} but it did!"
42
+ end
43
+
44
+ end
@@ -0,0 +1,46 @@
1
+ # If set to db, then use DB table to store gateways,
2
+ # useful when your run many gateways on the same server.
3
+ gateways_source: config
4
+
5
+ gateways:
6
+ default:
7
+ pubkey: xpub-xxx # <- TODO: change this to your BIP32 pubkey
8
+ confirmations_required: 0
9
+ order_class: "StraightServer::Order"
10
+ secret: 'secret'
11
+ check_signature: false
12
+ callback_url: 'http://localhost:3000/my_app/payment_callback'
13
+ default_currency: 'BTC'
14
+
15
+ # The order matters here, we check for prices with the first adapter,
16
+ # if it fails, move on to the next
17
+ exchange_rate_adapters:
18
+ - Bitpay
19
+ - Coinbase
20
+ - Bitstamp
21
+
22
+ logmaster:
23
+ log_level: INFO # Wise to change to WARN for production
24
+ file: straight.log
25
+ raise_exception: false
26
+ name: Straight server logger
27
+
28
+ # These options bellow send you email whenever a FATAL error occurs.
29
+ # You probably want to uncomment them for production. See https://github.com/snitko/logmaste
30
+ # for various email options.
31
+ #
32
+ #email_config:
33
+ #to: 'me@email.foo',
34
+ #from: "logmaster@yourapp.com"
35
+
36
+ db:
37
+ adapter: sqlite
38
+ name: straight.db # file is always located in ~/.straight
39
+
40
+ # No need to set these options for sqlite,
41
+ # but other DBs may require them.
42
+ #
43
+ #user: username
44
+ #password: password
45
+ #host: hostname
46
+ #port: 1234