straight-server 0.2.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/Gemfile +21 -16
- data/Gemfile.lock +44 -30
- data/Gemfile.travis +15 -16
- data/README.md +66 -47
- data/VERSION +1 -1
- data/db/migrations/011_add_callback_data_to_orders.rb +1 -1
- data/db/migrations/012_add_address_provider.rb +11 -0
- data/db/migrations/013_add_address_derivation_scheme.rb +11 -0
- data/db/migrations/014_pubkey_null_address_provider_not_null.rb +8 -0
- data/db/migrations/015_add_amount_paid_to_orders.rb +11 -0
- data/db/migrations/016_add_new_params_to_orders.rb +13 -0
- data/db/migrations/017_add_test_mode_to_gateways.rb +11 -0
- data/db/migrations/018_add_test_keychain_id_to_gateways.rb +11 -0
- data/db/migrations/019_add_test_pubkey_to_gateways.rb +11 -0
- data/db/migrations/020_add_test_mode_to_orders.rb +11 -0
- data/db/schema.rb +11 -1
- data/lib/straight-server.rb +11 -9
- data/lib/straight-server/config.rb +28 -18
- data/lib/straight-server/gateway.rb +167 -87
- data/lib/straight-server/initializer.rb +13 -7
- data/lib/straight-server/order.rb +39 -17
- data/lib/straight-server/orders_controller.rb +71 -21
- data/lib/straight-server/random_string.rb +3 -13
- data/lib/straight-server/server.rb +3 -4
- data/lib/straight-server/signature_validator.rb +69 -0
- data/lib/straight-server/thread.rb +19 -4
- data/lib/straight-server/throttler.rb +7 -13
- data/lib/tasks/db.rake +1 -1
- data/spec/.straight/config.yml +8 -3
- data/spec/.straight/default_test_last_keychain_id +1 -0
- data/spec/factories.rb +2 -1
- data/spec/lib/gateway_spec.rb +222 -94
- data/spec/lib/initializer_spec.rb +1 -1
- data/spec/lib/order_spec.rb +26 -7
- data/spec/lib/orders_controller_spec.rb +65 -6
- data/spec/lib/signature_validator_spec.rb +72 -0
- data/spec/lib/thread_spec.rb +16 -0
- data/spec/lib/throttle_spec.rb +2 -2
- data/spec/spec_helper.rb +17 -22
- data/straight-server.gemspec +31 -12
- data/templates/config.yml +19 -10
- metadata +52 -11
@@ -41,12 +41,12 @@ module StraightServer
|
|
41
41
|
create_logger
|
42
42
|
connect_to_db
|
43
43
|
run_migrations if migrations_pending?
|
44
|
-
setup_redis_connection
|
44
|
+
setup_redis_connection
|
45
45
|
initialize_routes
|
46
46
|
end
|
47
47
|
|
48
48
|
def add_route(path, &block)
|
49
|
-
@routes[path] = block
|
49
|
+
@routes[path] = block
|
50
50
|
end
|
51
51
|
|
52
52
|
def create_config_files
|
@@ -116,6 +116,7 @@ module StraightServer
|
|
116
116
|
end
|
117
117
|
|
118
118
|
def create_logger
|
119
|
+
return unless Config.logmaster
|
119
120
|
require_relative 'logger'
|
120
121
|
StraightServer.logger = StraightServer::Logger.new(
|
121
122
|
log_level: ::Logger.const_get(Config.logmaster['log_level'].upcase),
|
@@ -128,7 +129,11 @@ module StraightServer
|
|
128
129
|
|
129
130
|
def initialize_routes
|
130
131
|
@routes = {}
|
131
|
-
add_route
|
132
|
+
add_route %r{\A/gateways/.+?/orders(/.+)?\Z} do |env|
|
133
|
+
controller = OrdersController.new(env)
|
134
|
+
controller.response
|
135
|
+
end
|
136
|
+
add_route %r{\A/gateways/.+?/last_keychain_id\Z} do |env|
|
132
137
|
controller = OrdersController.new(env)
|
133
138
|
controller.response
|
134
139
|
end
|
@@ -168,6 +173,7 @@ module StraightServer
|
|
168
173
|
# an unclean shutdown of the server. Let's check and update the status manually once.
|
169
174
|
if order.time_left_before_expiration < 1
|
170
175
|
StraightServer.logger.info "Order #{order.id} seems to be expired, but status remains #{order.status}. Will check for status update manually."
|
176
|
+
order.gateway.test_mode = true if order.test_mode
|
171
177
|
order.status(reload: true)
|
172
178
|
|
173
179
|
# if we still see no transactions to that address,
|
@@ -187,17 +193,17 @@ module StraightServer
|
|
187
193
|
end
|
188
194
|
|
189
195
|
# Loads redis gem and sets up key prefixes for order counters
|
190
|
-
# for the current straight environment.
|
196
|
+
# for the current straight environment.
|
191
197
|
def setup_redis_connection
|
192
|
-
|
198
|
+
raise "Redis not configured" unless Config.redis
|
193
199
|
Config.redis = Config.redis.keys_to_sym
|
194
|
-
Config.redis[:
|
200
|
+
Config.redis[:prefix] ||= "StraightServer:#{Config.environment}"
|
201
|
+
StraightServer.redis_connection = Redis.new(
|
195
202
|
host: Config.redis[:host],
|
196
203
|
port: Config.redis[:port],
|
197
204
|
db: Config.redis[:db],
|
198
205
|
password: Config.redis[:password]
|
199
206
|
)
|
200
|
-
Config.redis[:prefix] ||= "StraightServer:#{Config.environment}"
|
201
207
|
end
|
202
208
|
|
203
209
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module StraightServer
|
2
|
-
|
2
|
+
|
3
3
|
class Order < Sequel::Model
|
4
4
|
|
5
5
|
include Straight::OrderModule
|
@@ -10,12 +10,12 @@ module StraightServer
|
|
10
10
|
|
11
11
|
# Additional data that can be passed and stored with each order. Not returned with the callback.
|
12
12
|
serialize_attributes :marshal, :data
|
13
|
-
|
13
|
+
|
14
14
|
# data that was provided by the merchan upon order creation and is sent back with the callback
|
15
|
-
serialize_attributes :marshal, :callback_data
|
15
|
+
serialize_attributes :marshal, :callback_data
|
16
16
|
|
17
17
|
# stores the response of the server to which the callback is issued
|
18
|
-
serialize_attributes :marshal, :callback_response
|
18
|
+
serialize_attributes :marshal, :callback_response
|
19
19
|
|
20
20
|
plugin :after_initialize
|
21
21
|
def after_initialize
|
@@ -25,7 +25,7 @@ module StraightServer
|
|
25
25
|
def gateway
|
26
26
|
@gateway ||= Gateway.find_by_id(gateway_id)
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
def gateway=(g)
|
30
30
|
self.gateway_id = g.id
|
31
31
|
@gateway = g
|
@@ -60,13 +60,30 @@ module StraightServer
|
|
60
60
|
self[:status] = @status
|
61
61
|
end
|
62
62
|
|
63
|
+
def cancelable?
|
64
|
+
status == Straight::Order::STATUSES.fetch(:new)
|
65
|
+
end
|
66
|
+
|
67
|
+
def cancel
|
68
|
+
self.status = Straight::Order::STATUSES.fetch(:canceled)
|
69
|
+
save
|
70
|
+
StraightServer::Thread.interrupt(label: payment_id)
|
71
|
+
end
|
72
|
+
|
63
73
|
def save
|
64
74
|
super # calling Sequel::Model save
|
65
75
|
@status_changed = false
|
66
76
|
end
|
67
77
|
|
68
78
|
def to_h
|
69
|
-
super.merge({
|
79
|
+
super.merge({
|
80
|
+
id: id,
|
81
|
+
payment_id: payment_id,
|
82
|
+
amount_in_btc: amount_in_btc(as: :string),
|
83
|
+
amount_paid_in_btc: amount_in_btc(field: amount_paid,as: :string),
|
84
|
+
keychain_id: keychain_id,
|
85
|
+
last_keychain_id: (self.gateway.test_mode ? self.gateway.test_last_keychain_id : self.gateway.last_keychain_id)
|
86
|
+
})
|
70
87
|
end
|
71
88
|
|
72
89
|
def to_json
|
@@ -75,17 +92,18 @@ module StraightServer
|
|
75
92
|
|
76
93
|
def validate
|
77
94
|
super # calling Sequel::Model validator
|
78
|
-
errors.add(:amount,
|
79
|
-
errors.add(:amount,
|
80
|
-
errors.add(:
|
81
|
-
errors.add(:
|
82
|
-
errors.add(:
|
83
|
-
|
95
|
+
errors.add(:amount, "is not numeric") if !amount.kind_of?(Numeric)
|
96
|
+
errors.add(:amount, "should be more than 0") if amount && amount <= 0
|
97
|
+
errors.add(:amount_paid, "is not numeric") if !amount.kind_of?(Numeric)
|
98
|
+
errors.add(:gateway_id, "is invalid") if !gateway_id.kind_of?(Numeric) || gateway_id <= 0
|
99
|
+
errors.add(:description, "should be shorter than 256 characters") if description.kind_of?(String) && description.length > 255
|
100
|
+
errors.add(:gateway, "is inactive, cannot create order for inactive gateway") if !gateway.active && self.new?
|
101
|
+
validates_unique :id
|
84
102
|
validates_presence [:address, :keychain_id, :gateway_id, :amount]
|
85
103
|
end
|
86
104
|
|
87
105
|
def to_http_params
|
88
|
-
"order_id=#{id}&amount=#{amount}&amount_in_btc=#{amount_in_btc(as: :string)}&status=#{status}&address=#{address}&tid=#{tid}&keychain_id=#{keychain_id}&last_keychain_id=#{@gateway.last_keychain_id}"
|
106
|
+
"order_id=#{id}&amount=#{amount}&amount_in_btc=#{amount_in_btc(as: :string)}&amount_paid_in_btc=#{amount_in_btc(field: amount_paid, as: :string)}&status=#{status}&address=#{address}&tid=#{tid}&keychain_id=#{keychain_id}&last_keychain_id=#{@gateway.last_keychain_id}"
|
89
107
|
end
|
90
108
|
|
91
109
|
def before_create
|
@@ -96,13 +114,13 @@ module StraightServer
|
|
96
114
|
self.data = {} unless self.data
|
97
115
|
self.data[:exchange_rate] = { price: gateway.current_exchange_rate, currency: gateway.default_currency }
|
98
116
|
end
|
99
|
-
|
117
|
+
|
100
118
|
super
|
101
119
|
end
|
102
120
|
|
103
121
|
# Update Gateway's order_counters, incrementing the :new counter.
|
104
122
|
# All other increments/decrements happen in the the Gateway#order_status_changed callback,
|
105
|
-
# but the initial :new increment needs this code because the Gateway#order_status_changed
|
123
|
+
# but the initial :new increment needs this code because the Gateway#order_status_changed
|
106
124
|
# isn't called in this case.
|
107
125
|
def after_create
|
108
126
|
self.gateway.increment_order_counter!(:new) if StraightServer::Config.count_orders
|
@@ -112,15 +130,19 @@ module StraightServer
|
|
112
130
|
# Order#created_at into account now, so that we don't start checking on
|
113
131
|
# an order that is already expired. Or, if it's not expired yet,
|
114
132
|
# we make sure to stop all checks as soon as it expires, but not later.
|
115
|
-
def start_periodic_status_check
|
116
|
-
StraightServer.logger.info "Starting periodic status checks of order #{self.id} (expires in #{duration} seconds)"
|
133
|
+
def start_periodic_status_check
|
117
134
|
if (t = time_left_before_expiration) > 0
|
135
|
+
StraightServer.logger.info "Starting periodic status checks of order #{id} (expires in #{t} seconds)"
|
118
136
|
check_status_on_schedule(duration: t)
|
119
137
|
end
|
120
138
|
self.save if self.status_changed?
|
121
139
|
end
|
122
140
|
|
123
141
|
def check_status_on_schedule(period: 10, iteration_index: 0, duration: 600, time_passed: 0)
|
142
|
+
if StraightServer::Thread.interrupted?(thread: ::Thread.current)
|
143
|
+
StraightServer.logger.info "Checking status of order #{self.id} interrupted"
|
144
|
+
return
|
145
|
+
end
|
124
146
|
StraightServer.logger.info "Checking status of order #{self.id}"
|
125
147
|
super
|
126
148
|
end
|
@@ -1,8 +1,10 @@
|
|
1
|
-
require_relative '
|
1
|
+
require_relative 'throttler'
|
2
|
+
require_relative 'signature_validator'
|
2
3
|
|
3
4
|
module StraightServer
|
4
5
|
|
5
6
|
class OrdersController
|
7
|
+
include Goliath::Constants
|
6
8
|
|
7
9
|
attr_reader :response
|
8
10
|
|
@@ -21,7 +23,9 @@ module StraightServer
|
|
21
23
|
return [404, {}, "Gateway not found" ]
|
22
24
|
end
|
23
25
|
|
24
|
-
|
26
|
+
if @gateway.check_signature
|
27
|
+
StraightServer::SignatureValidator.new(@gateway, @env).validate!
|
28
|
+
else
|
25
29
|
ip = @env['HTTP_X_FORWARDED_FOR'].to_s
|
26
30
|
ip = @env['REMOTE_ADDR'] if ip.empty?
|
27
31
|
if StraightServer::Throttler.new(@gateway.id).deny?(ip)
|
@@ -43,20 +47,20 @@ module StraightServer
|
|
43
47
|
currency: @params['currency'],
|
44
48
|
btc_denomination: @params['btc_denomination'],
|
45
49
|
keychain_id: @params['keychain_id'],
|
46
|
-
signature: @params['signature'],
|
47
50
|
callback_data: @params['callback_data'],
|
48
|
-
data: @params['data']
|
51
|
+
data: @params['data'],
|
52
|
+
description: @params['description']
|
49
53
|
}
|
54
|
+
|
50
55
|
order = @gateway.create_order(order_data)
|
51
|
-
StraightServer::Thread.new do
|
56
|
+
StraightServer::Thread.new(label: order.payment_id) do
|
52
57
|
# Because this is a new thread, we have to wrap the code inside in #watch_exceptions
|
53
58
|
# once again. Otherwise, no watching is done. Oh, threads!
|
54
59
|
StraightServer.logger.watch_exceptions do
|
55
60
|
order.start_periodic_status_check
|
56
61
|
end
|
57
62
|
end
|
58
|
-
|
59
|
-
[200, {}, order.to_json ]
|
63
|
+
[200, {}, add_callback_data_warning(order).to_json]
|
60
64
|
rescue Sequel::ValidationFailed => e
|
61
65
|
StraightServer.logger.warn(
|
62
66
|
"VALIDATION ERRORS in order, cannot create it:\n" +
|
@@ -64,11 +68,8 @@ module StraightServer
|
|
64
68
|
"Order data: #{order_data.inspect}\n"
|
65
69
|
)
|
66
70
|
[409, {}, "Invalid order: #{e.message}" ]
|
67
|
-
rescue
|
68
|
-
[409, {}, "Invalid
|
69
|
-
rescue StraightServer::GatewayModule::InvalidOrderId
|
70
|
-
StraightServer.logger.warn message = "An invalid id for order supplied: #{@params['order_id']}"
|
71
|
-
[409, {}, message ]
|
71
|
+
rescue Straight::Gateway::OrderAmountInvalid => e
|
72
|
+
[409, {}, "Invalid order: #{e.message}" ]
|
72
73
|
rescue StraightServer::GatewayModule::GatewayInactive
|
73
74
|
StraightServer.logger.warn message = "The gateway is inactive, you cannot create order with it"
|
74
75
|
[503, {}, message ]
|
@@ -82,6 +83,10 @@ module StraightServer
|
|
82
83
|
return [404, {}, "Gateway not found" ]
|
83
84
|
end
|
84
85
|
|
86
|
+
if @gateway.check_signature
|
87
|
+
StraightServer::SignatureValidator.new(@gateway, @env).validate!
|
88
|
+
end
|
89
|
+
|
85
90
|
order = find_order
|
86
91
|
|
87
92
|
if order
|
@@ -106,8 +111,40 @@ module StraightServer
|
|
106
111
|
end
|
107
112
|
end
|
108
113
|
|
114
|
+
def cancel
|
115
|
+
unless @gateway
|
116
|
+
StraightServer.logger.warn "Gateway not found"
|
117
|
+
return [404, {}, "Gateway not found"]
|
118
|
+
end
|
119
|
+
|
120
|
+
if @gateway.check_signature
|
121
|
+
StraightServer::SignatureValidator.new(@gateway, @env).validate!
|
122
|
+
end
|
123
|
+
|
124
|
+
if (order = find_order)
|
125
|
+
order.status(reload: true)
|
126
|
+
order.save if order.status_changed?
|
127
|
+
if order.cancelable?
|
128
|
+
order.cancel
|
129
|
+
[200, {}, '']
|
130
|
+
else
|
131
|
+
[409, {}, "Order is not cancelable"]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def last_keychain_id
|
137
|
+
unless @gateway
|
138
|
+
StraightServer.logger.warn "Gateway not foun"
|
139
|
+
return [404, {}, "Gateway not found"]
|
140
|
+
end
|
141
|
+
|
142
|
+
[200, {}, {gateway_id: @gateway.id, last_keychain_id: @gateway.last_keychain_id}.to_json]
|
143
|
+
end
|
144
|
+
|
109
145
|
private
|
110
146
|
|
147
|
+
# Refactoring proposed: https://github.com/AlexanderPavlenko/straight-server/commit/49ea6e3732a9564c04d8dfecaee6d0ebaa462042
|
111
148
|
def dispatch
|
112
149
|
|
113
150
|
StraightServer.logger.blank_lines
|
@@ -115,17 +152,30 @@ module StraightServer
|
|
115
152
|
|
116
153
|
@gateway = StraightServer::Gateway.find_by_hashed_id(@request_path[1])
|
117
154
|
|
118
|
-
@response =
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
websocket
|
123
|
-
|
124
|
-
|
155
|
+
@response = begin
|
156
|
+
if @request_path[3] # if an order id is supplied
|
157
|
+
@params['id'] = @request_path[3]
|
158
|
+
@params['id'] = @params['id'].to_i if @params['id'] =~ /\A\d+\Z/
|
159
|
+
if @request_path[4] == 'websocket'
|
160
|
+
websocket
|
161
|
+
elsif @request_path[4] == 'cancel'&& @method == 'POST'
|
162
|
+
cancel
|
163
|
+
elsif @request_path[4].nil? && @method == 'GET'
|
164
|
+
show
|
165
|
+
end
|
166
|
+
elsif @request_path[2] == 'last_keychain_id'
|
167
|
+
last_keychain_id
|
168
|
+
elsif @request_path[3].nil?# && @method == 'POST'
|
169
|
+
create
|
125
170
|
end
|
126
|
-
|
127
|
-
|
171
|
+
rescue StraightServer::SignatureValidator::InvalidNonce
|
172
|
+
StraightServer.logger.warn message = "X-Nonce is invalid: #{@env["#{HTTP_PREFIX}X_NONCE"].inspect}"
|
173
|
+
[409, {}, message]
|
174
|
+
rescue StraightServer::SignatureValidator::InvalidSignature
|
175
|
+
StraightServer.logger.warn message = "X-Signature is invalid: #{@env["#{HTTP_PREFIX}X_SIGNATURE"].inspect}"
|
176
|
+
[409, {}, message]
|
128
177
|
end
|
178
|
+
|
129
179
|
@response = [404, {}, "#{@method} /#{@request_path.join('/')} Not found"] if @response.nil?
|
130
180
|
end
|
131
181
|
|
@@ -1,18 +1,8 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
1
3
|
class String
|
2
4
|
|
3
5
|
def self.random(len)
|
4
|
-
|
5
|
-
while s.length != len do
|
6
|
-
s = rand(36**len).to_s(36)
|
7
|
-
end
|
8
|
-
s
|
9
|
-
end
|
10
|
-
|
11
|
-
def repeat(times)
|
12
|
-
result = ""
|
13
|
-
times.times { result << self }
|
14
|
-
result
|
6
|
+
BTC::Base58.base58_from_data(SecureRandom.random_bytes(len))[0, len]
|
15
7
|
end
|
16
|
-
|
17
8
|
end
|
18
|
-
|
@@ -11,7 +11,6 @@ module StraightServer
|
|
11
11
|
StraightServer.logger.info "starting Straight Server v #{StraightServer::VERSION}"
|
12
12
|
require_relative 'order'
|
13
13
|
require_relative 'gateway'
|
14
|
-
require_relative 'orders_controller'
|
15
14
|
load_addons
|
16
15
|
resume_tracking_active_orders!
|
17
16
|
end
|
@@ -20,7 +19,7 @@ module StraightServer
|
|
20
19
|
# Even though we define that option here, it is purely for the purposes of compliance with
|
21
20
|
# Goliath server. If don't do that, there will be an exception saying "unrecognized argument".
|
22
21
|
# In reality, we make use of --config-dir value in the in StraightServer::Initializer and stored
|
23
|
-
# it in StraightServer::Initializer.config_dir property.
|
22
|
+
# it in StraightServer::Initializer.config_dir property.
|
24
23
|
opts.on('-c', '--config-dir STRING', "Directory where config files and addons are placed") do |val|
|
25
24
|
options[:config_dir] = File.expand_path(val || ENV['HOME'] + '/.straight' )
|
26
25
|
end
|
@@ -42,7 +41,7 @@ module StraightServer
|
|
42
41
|
# AFTER the process is daemonized, this shall remain as it is now.
|
43
42
|
begin
|
44
43
|
return process_request(env)
|
45
|
-
rescue Sequel::DatabaseDisconnectError
|
44
|
+
rescue Sequel::DatabaseDisconnectError
|
46
45
|
connect_to_db
|
47
46
|
return process_request(env)
|
48
47
|
end
|
@@ -74,6 +73,6 @@ module StraightServer
|
|
74
73
|
# no block was called, means no route matched. Let's render 404
|
75
74
|
return [404, {}, "#{env['REQUEST_METHOD']} #{env['REQUEST_PATH']} Not found"]
|
76
75
|
end
|
77
|
-
|
76
|
+
|
78
77
|
end
|
79
78
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'goliath/constants'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
module StraightServer
|
5
|
+
class SignatureValidator
|
6
|
+
include Goliath::Constants
|
7
|
+
|
8
|
+
SignatureValidatorError = Class.new(StandardError)
|
9
|
+
InvalidNonce = Class.new(SignatureValidatorError)
|
10
|
+
InvalidSignature = Class.new(SignatureValidatorError)
|
11
|
+
|
12
|
+
attr_reader :gateway, :env
|
13
|
+
|
14
|
+
def initialize(gateway, env)
|
15
|
+
@gateway = gateway
|
16
|
+
@env = env
|
17
|
+
end
|
18
|
+
|
19
|
+
def validate!
|
20
|
+
raise InvalidNonce unless valid_nonce?
|
21
|
+
raise InvalidSignature unless valid_signature?
|
22
|
+
true
|
23
|
+
end
|
24
|
+
|
25
|
+
def valid_nonce?
|
26
|
+
nonce = env["#{HTTP_PREFIX}X_NONCE"].to_i
|
27
|
+
redis = StraightServer.redis_connection
|
28
|
+
loop do
|
29
|
+
redis.watch last_nonce_key do
|
30
|
+
last_nonce = redis.get(last_nonce_key).to_i
|
31
|
+
if last_nonce < nonce
|
32
|
+
result = redis.multi do |multi|
|
33
|
+
multi.set last_nonce_key, nonce
|
34
|
+
end
|
35
|
+
return true if result[0] == 'OK'
|
36
|
+
else
|
37
|
+
redis.unwatch
|
38
|
+
return false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def valid_signature?
|
45
|
+
signature == env["#{HTTP_PREFIX}X_SIGNATURE"]
|
46
|
+
end
|
47
|
+
|
48
|
+
def last_nonce_key
|
49
|
+
"#{Config[:'redis.prefix']}:LastNonce:#{gateway.id}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def signature
|
53
|
+
self.class.signature(
|
54
|
+
nonce: env["#{HTTP_PREFIX}X_NONCE"],
|
55
|
+
body: env[RACK_INPUT].kind_of?(StringIO) ? env[RACK_INPUT].string : env[RACK_INPUT].to_s,
|
56
|
+
method: env[REQUEST_METHOD],
|
57
|
+
request_uri: env[REQUEST_URI],
|
58
|
+
secret: gateway.secret,
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Should mirror StraightServerKit.signature
|
63
|
+
def self.signature(nonce:, body:, method:, request_uri:, secret:)
|
64
|
+
sha512 = OpenSSL::Digest::SHA512.new
|
65
|
+
request = "#{method.to_s.upcase}#{request_uri}#{sha512.digest("#{nonce}#{body}")}"
|
66
|
+
Base64.strict_encode64 OpenSSL::HMAC.digest(sha512, secret.to_s, request)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|