lighstorm 0.0.7 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.env.example +1 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +1 -0
- data/Gemfile +5 -3
- data/Gemfile.lock +15 -10
- data/README.md +4 -3
- data/Rakefile +11 -0
- data/adapters/edges/payment/purpose.rb +22 -11
- data/adapters/edges/payment.rb +51 -12
- data/adapters/invoice.rb +139 -17
- data/components/cache.rb +7 -2
- data/controllers/forward/group_by_channel.rb +1 -1
- data/controllers/invoice/actions/create.rb +22 -6
- data/controllers/invoice/actions/pay.rb +71 -13
- data/controllers/invoice/all.rb +16 -6
- data/controllers/invoice/decode.rb +44 -0
- data/controllers/invoice/find_by_secret_hash.rb +7 -1
- data/controllers/invoice.rb +17 -3
- data/controllers/node/actions/pay.rb +109 -0
- data/controllers/payment/actions/pay.rb +104 -0
- data/controllers/payment/all.rb +49 -16
- data/controllers/transaction/all.rb +54 -0
- data/controllers/transaction.rb +13 -0
- data/deleted.sh +1 -0
- data/docs/README.md +292 -49
- data/docs/_coverpage.md +1 -1
- data/docs/index.html +1 -1
- data/helpers/time_expression.rb +33 -0
- data/models/connections/payment_channel.rb +13 -8
- data/models/edges/channel.rb +1 -1
- data/models/edges/payment.rb +51 -20
- data/models/errors.rb +29 -1
- data/models/invoice.rb +67 -11
- data/models/nodes/node.rb +32 -0
- data/models/satoshis.rb +11 -3
- data/models/secret.rb +31 -0
- data/models/transaction.rb +42 -0
- data/ports/dsl/lighstorm.rb +2 -0
- data/static/cache.rb +14 -13
- data/static/spec.rb +1 -1
- metadata +12 -4
- data/adapters/payment_request.rb +0 -87
- data/models/payment_request.rb +0 -72
data/controllers/invoice/all.rb
CHANGED
@@ -8,7 +8,9 @@ module Lighstorm
|
|
8
8
|
module Controllers
|
9
9
|
module Invoice
|
10
10
|
module All
|
11
|
-
def self.fetch(limit: nil)
|
11
|
+
def self.fetch(limit: nil, spontaneous: false)
|
12
|
+
at = Time.now
|
13
|
+
|
12
14
|
last_offset = 0
|
13
15
|
|
14
16
|
invoices = []
|
@@ -19,7 +21,9 @@ module Lighstorm
|
|
19
21
|
num_max_invoices: 10
|
20
22
|
)
|
21
23
|
|
22
|
-
response.invoices.each
|
24
|
+
response.invoices.each do |invoice|
|
25
|
+
invoices << invoice.to_h if spontaneous || !invoice.payment_request.empty?
|
26
|
+
end
|
23
27
|
|
24
28
|
# TODO: How to optimize this?
|
25
29
|
# break if !limit.nil? && invoices.size >= limit
|
@@ -33,13 +37,15 @@ module Lighstorm
|
|
33
37
|
|
34
38
|
invoices = invoices[0..limit - 1] unless limit.nil?
|
35
39
|
|
36
|
-
{ list_invoices: invoices }
|
40
|
+
{ at: at, list_invoices: invoices }
|
37
41
|
end
|
38
42
|
|
39
43
|
def self.adapt(raw)
|
44
|
+
raise 'missing at' if raw[:at].nil?
|
45
|
+
|
40
46
|
{
|
41
47
|
list_invoices: raw[:list_invoices].map do |raw_invoice|
|
42
|
-
Lighstorm::Adapter::Invoice.list_invoices(raw_invoice)
|
48
|
+
Lighstorm::Adapter::Invoice.list_invoices(raw_invoice, raw[:at])
|
43
49
|
end
|
44
50
|
}
|
45
51
|
end
|
@@ -51,8 +57,12 @@ module Lighstorm
|
|
51
57
|
end
|
52
58
|
end
|
53
59
|
|
54
|
-
def self.data(limit: nil, &vcr)
|
55
|
-
raw = vcr.nil?
|
60
|
+
def self.data(limit: nil, spontaneous: false, &vcr)
|
61
|
+
raw = if vcr.nil?
|
62
|
+
fetch(limit: limit, spontaneous: spontaneous)
|
63
|
+
else
|
64
|
+
vcr.call(-> { fetch(limit: limit, spontaneous: spontaneous) })
|
65
|
+
end
|
56
66
|
|
57
67
|
adapted = adapt(raw)
|
58
68
|
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../ports/grpc'
|
4
|
+
require_relative '../../adapters/invoice'
|
5
|
+
require_relative '../../models/invoice'
|
6
|
+
|
7
|
+
module Lighstorm
|
8
|
+
module Controllers
|
9
|
+
module Invoice
|
10
|
+
module Decode
|
11
|
+
def self.fetch(request_code)
|
12
|
+
{
|
13
|
+
_request_code: request_code,
|
14
|
+
decode_pay_req: Ports::GRPC.lightning.decode_pay_req(pay_req: request_code).to_h
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.adapt(raw)
|
19
|
+
{
|
20
|
+
decode_pay_req: Lighstorm::Adapter::Invoice.decode_pay_req(
|
21
|
+
raw[:decode_pay_req], raw[:_request_code]
|
22
|
+
)
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.transform(adapted)
|
27
|
+
adapted[:decode_pay_req]
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.data(request_code, &vcr)
|
31
|
+
raw = vcr.nil? ? fetch(request_code) : vcr.call(-> { fetch(request_code) })
|
32
|
+
|
33
|
+
adapted = adapt(raw)
|
34
|
+
|
35
|
+
transform(adapted)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.model(data)
|
39
|
+
Lighstorm::Models::Invoice.new(data)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -10,13 +10,19 @@ module Lighstorm
|
|
10
10
|
module FindBySecretHash
|
11
11
|
def self.fetch(secret_hash)
|
12
12
|
{
|
13
|
+
at: Time.now,
|
13
14
|
lookup_invoice: Ports::GRPC.lightning.lookup_invoice(r_hash_str: secret_hash).to_h
|
14
15
|
}
|
15
16
|
end
|
16
17
|
|
17
18
|
def self.adapt(raw)
|
19
|
+
raise 'missing at' if raw[:at].nil?
|
20
|
+
|
18
21
|
{
|
19
|
-
lookup_invoice: Lighstorm::Adapter::Invoice.lookup_invoice(
|
22
|
+
lookup_invoice: Lighstorm::Adapter::Invoice.lookup_invoice(
|
23
|
+
raw[:lookup_invoice],
|
24
|
+
raw[:at]
|
25
|
+
)
|
20
26
|
}
|
21
27
|
end
|
22
28
|
|
data/controllers/invoice.rb
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative './invoice/all'
|
4
|
+
require_relative './invoice/decode'
|
4
5
|
require_relative './invoice/find_by_secret_hash'
|
5
6
|
require_relative './invoice/actions/create'
|
6
7
|
|
7
8
|
module Lighstorm
|
8
9
|
module Controllers
|
9
10
|
module Invoice
|
10
|
-
def self.all(limit: nil)
|
11
|
-
All.model(All.data(limit: limit))
|
11
|
+
def self.all(limit: nil, spontaneous: false)
|
12
|
+
All.model(All.data(limit: limit, spontaneous: spontaneous))
|
12
13
|
end
|
13
14
|
|
14
15
|
def self.first
|
@@ -23,10 +24,23 @@ module Lighstorm
|
|
23
24
|
FindBySecretHash.model(FindBySecretHash.data(secret_hash))
|
24
25
|
end
|
25
26
|
|
26
|
-
def self.
|
27
|
+
def self.decode(request_code, &vcr)
|
28
|
+
Decode.model(Decode.data(request_code, &vcr))
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.create(
|
32
|
+
payable:,
|
33
|
+
description: nil, millisatoshis: nil,
|
34
|
+
# Lightning Invoice Expiration: UX Considerations
|
35
|
+
# https://d.elor.me/2022/01/lightning-invoice-expiration-ux-considerations/
|
36
|
+
expires_in: { hours: 24 },
|
37
|
+
preview: false, &vcr
|
38
|
+
)
|
27
39
|
Create.perform(
|
40
|
+
payable: payable,
|
28
41
|
description: description,
|
29
42
|
millisatoshis: millisatoshis,
|
43
|
+
expires_in: expires_in,
|
30
44
|
preview: preview,
|
31
45
|
&vcr
|
32
46
|
)
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'securerandom'
|
4
|
+
require 'digest'
|
5
|
+
|
6
|
+
require_relative '../../../ports/grpc'
|
7
|
+
require_relative '../../../models/secret'
|
8
|
+
require_relative '../../../models/errors'
|
9
|
+
require_relative '../../../models/edges/payment'
|
10
|
+
require_relative '../../../adapters/edges/payment'
|
11
|
+
require_relative '../../invoice'
|
12
|
+
require_relative '../../action'
|
13
|
+
require_relative '../../node/myself'
|
14
|
+
|
15
|
+
require_relative '../../payment/actions/pay'
|
16
|
+
|
17
|
+
module Lighstorm
|
18
|
+
module Controllers
|
19
|
+
module Node
|
20
|
+
module Pay
|
21
|
+
def self.dispatch(grpc_request, &vcr)
|
22
|
+
Payment::Pay.dispatch(grpc_request, &vcr)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.fetch(&vcr)
|
26
|
+
Payment::Pay.fetch(&vcr)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.adapt(data, node_get_info)
|
30
|
+
Payment::Pay.adapt(data, node_get_info)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.model(data)
|
34
|
+
Payment::Pay.model(data)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.prepare(public_key:, millisatoshis:, times_out_in:, secret:, through:, message: nil)
|
38
|
+
# Appreciation note for people that suffered in the past and shared
|
39
|
+
# their knowledge, so we don't have to struggle the same:
|
40
|
+
# - https://github.com/lightningnetwork/lnd/discussions/6357
|
41
|
+
# - https://docs.lightning.engineering/lightning-network-tools/lnd/send-messages-with-keysend
|
42
|
+
# - https://peakd.com/@brianoflondon/lightning-keysend-is-strange-and-how-to-send-keysend-payment-in-lightning-with-the-lnd-rest-api-via-python
|
43
|
+
# We are standing on the shoulders of giants, thank you very much. :)
|
44
|
+
request = {
|
45
|
+
service: :router,
|
46
|
+
method: :send_payment_v2,
|
47
|
+
params: {
|
48
|
+
dest: [public_key].pack('H*'),
|
49
|
+
amt_msat: millisatoshis,
|
50
|
+
timeout_seconds: Helpers::TimeExpression.seconds(times_out_in),
|
51
|
+
allow_self_payment: true,
|
52
|
+
dest_custom_records: {}
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
if !message.nil? && !message.empty?
|
57
|
+
# https://github.com/satoshisstream/satoshis.stream/blob/main/TLV_registry.md
|
58
|
+
request[:params][:dest_custom_records][34_349_334] = message
|
59
|
+
end
|
60
|
+
|
61
|
+
if through.to_sym == :keysend
|
62
|
+
request[:params][:payment_hash] = [secret[:hash]].pack('H*')
|
63
|
+
request[:params][:dest_custom_records][5_482_373_484] = [secret[:preimage]].pack('H*')
|
64
|
+
elsif through.to_sym == :amp
|
65
|
+
request[:params][:amp] = true
|
66
|
+
end
|
67
|
+
|
68
|
+
request[:params].delete(:dest_custom_records) if request[:params][:dest_custom_records].empty?
|
69
|
+
|
70
|
+
request
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.perform(
|
74
|
+
public_key:, millisatoshis:, through:,
|
75
|
+
times_out_in:,
|
76
|
+
message: nil, secret: nil,
|
77
|
+
preview: false, &vcr
|
78
|
+
)
|
79
|
+
secret = Models::Secret.create.to_h if secret.nil? && through.to_sym == :keysend
|
80
|
+
|
81
|
+
grpc_request = prepare(
|
82
|
+
public_key: public_key,
|
83
|
+
millisatoshis: millisatoshis,
|
84
|
+
through: through,
|
85
|
+
times_out_in: times_out_in,
|
86
|
+
secret: secret,
|
87
|
+
message: message
|
88
|
+
)
|
89
|
+
|
90
|
+
return grpc_request if preview
|
91
|
+
|
92
|
+
response = dispatch(grpc_request, &vcr)
|
93
|
+
|
94
|
+
Payment::Pay.raise_error_if_exists!(response)
|
95
|
+
|
96
|
+
data = fetch(&vcr)
|
97
|
+
|
98
|
+
adapted = adapt(response, data)
|
99
|
+
|
100
|
+
model = self.model(adapted)
|
101
|
+
|
102
|
+
Payment::Pay.raise_failure_if_exists!(model, response)
|
103
|
+
|
104
|
+
Action::Output.new({ response: response[:response], result: model })
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'securerandom'
|
4
|
+
require 'digest'
|
5
|
+
|
6
|
+
require_relative '../../../ports/grpc'
|
7
|
+
require_relative '../../../models/errors'
|
8
|
+
require_relative '../../../models/edges/payment'
|
9
|
+
require_relative '../../../adapters/edges/payment'
|
10
|
+
require_relative '../../node/myself'
|
11
|
+
require_relative '../../invoice/decode'
|
12
|
+
|
13
|
+
module Lighstorm
|
14
|
+
module Controllers
|
15
|
+
module Payment
|
16
|
+
module Pay
|
17
|
+
def self.call(grpc_request)
|
18
|
+
result = []
|
19
|
+
Lighstorm::Ports::GRPC.send(grpc_request[:service]).send(
|
20
|
+
grpc_request[:method], grpc_request[:params]
|
21
|
+
) do |response|
|
22
|
+
result << response.to_h
|
23
|
+
end
|
24
|
+
{ response: result, exception: nil }
|
25
|
+
rescue StandardError => e
|
26
|
+
{ exception: e }
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.dispatch(grpc_request, &vcr)
|
30
|
+
vcr.nil? ? call(grpc_request) : vcr.call(-> { call(grpc_request) }, :dispatch)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.fetch_all(request_code)
|
34
|
+
{
|
35
|
+
invoice_decode: request_code.nil? ? nil : Invoice::Decode.data(request_code),
|
36
|
+
node_myself: Node::Myself.data
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.fetch(request_code = nil, &vcr)
|
41
|
+
raw = vcr.nil? ? fetch_all(request_code) : vcr.call(-> { fetch_all(request_code) })
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.adapt(grpc_data, fetch_data)
|
45
|
+
Adapter::Payment.send_payment_v2(
|
46
|
+
grpc_data[:response].last,
|
47
|
+
fetch_data[:node_myself],
|
48
|
+
fetch_data[:invoice_decode]
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.model(data)
|
53
|
+
Models::Payment.new(data)
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.raise_error_if_exists!(response)
|
57
|
+
return if response[:exception].nil?
|
58
|
+
|
59
|
+
if response[:exception].is_a?(GRPC::AlreadyExists)
|
60
|
+
raise AlreadyPaidError.new(
|
61
|
+
'The invoice is already paid.',
|
62
|
+
grpc: response[:exception]
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
if response[:exception].message =~ /amount must not be specified when paying a non-zero/
|
67
|
+
raise AmountForNonZeroError.new(
|
68
|
+
'Millisatoshis must not be specified when paying a non-zero amount invoice.',
|
69
|
+
grpc: response[:exception]
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
if response[:exception].message =~ /amount must be specified when paying a zero amount/
|
74
|
+
raise MissingMillisatoshisError.new(
|
75
|
+
'Millisatoshis must be specified when paying a zero amount invoice.',
|
76
|
+
grpc: response[:exception]
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
raise PaymentError.new(
|
81
|
+
response[:exception].message,
|
82
|
+
grpc: response[:exception]
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.raise_failure_if_exists!(model, response)
|
87
|
+
return unless model.state == 'failed'
|
88
|
+
|
89
|
+
if response[:response].last[:failure_reason] == :FAILURE_REASON_NO_ROUTE
|
90
|
+
raise NoRouteFoundError.new(
|
91
|
+
response[:response].last[:failure_reason],
|
92
|
+
response: response[:response], result: model
|
93
|
+
)
|
94
|
+
else
|
95
|
+
raise PaymentError.new(
|
96
|
+
response[:response].last[:failure_reason],
|
97
|
+
response: response[:response], result: model
|
98
|
+
)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/controllers/payment/all.rb
CHANGED
@@ -195,7 +195,7 @@ module Lighstorm
|
|
195
195
|
raw[:decode_pay_req].each_key do |key|
|
196
196
|
next if raw[:decode_pay_req][key][:_error]
|
197
197
|
|
198
|
-
adapted[:decode_pay_req][key] = Lighstorm::Adapter::
|
198
|
+
adapted[:decode_pay_req][key] = Lighstorm::Adapter::Invoice.decode_pay_req(
|
199
199
|
raw[:decode_pay_req][key]
|
200
200
|
)
|
201
201
|
end
|
@@ -204,7 +204,7 @@ module Lighstorm
|
|
204
204
|
next if raw[:lookup_invoice][key][:_error]
|
205
205
|
|
206
206
|
adapted[:lookup_invoice][key] = Lighstorm::Adapter::Invoice.lookup_invoice(
|
207
|
-
raw[:lookup_invoice][key]
|
207
|
+
raw[:lookup_invoice][key], raw[:at]
|
208
208
|
)
|
209
209
|
end
|
210
210
|
|
@@ -309,34 +309,67 @@ module Lighstorm
|
|
309
309
|
end
|
310
310
|
|
311
311
|
def self.transform(list_payments, adapted)
|
312
|
-
if adapted[:lookup_invoice][list_payments[:
|
313
|
-
!adapted[:lookup_invoice][list_payments[:
|
312
|
+
if adapted[:lookup_invoice][list_payments[:secret][:hash]] &&
|
313
|
+
!adapted[:lookup_invoice][list_payments[:secret][:hash]][:_error]
|
314
314
|
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
list_payments[:
|
319
|
-
|
315
|
+
if list_payments[:invoice]
|
316
|
+
lookup = adapted[:lookup_invoice][list_payments[:secret][:hash]]
|
317
|
+
|
318
|
+
list_payments[:invoice][:description] = lookup[:description]
|
319
|
+
|
320
|
+
lookup.each_key do |key|
|
321
|
+
if lookup[key].is_a?(Hash)
|
322
|
+
unless list_payments[:invoice].key?(key) && !list_payments[:invoice][key].nil?
|
323
|
+
list_payments[:invoice][key] = lookup[:key]
|
324
|
+
end
|
325
|
+
|
326
|
+
next
|
327
|
+
end
|
328
|
+
|
329
|
+
unless list_payments[:invoice].key?(key) && !list_payments[:invoice][key].nil? &&
|
330
|
+
(!list_payments[:invoice][key].is_a?(String) || !list_payments[:invoice][key].empty?)
|
331
|
+
list_payments[:invoice][key] = lookup[key]
|
332
|
+
end
|
333
|
+
end
|
334
|
+
else
|
335
|
+
list_payments[:invoice] = adapted[:lookup_invoice][list_payments[:secret][:hash]]
|
336
|
+
end
|
320
337
|
end
|
338
|
+
|
321
339
|
list_payments[:hops].each do |hop|
|
322
340
|
hop[:channel] = transform_channel(hop[:channel], adapted)
|
323
341
|
end
|
324
342
|
|
325
|
-
if adapted[:decode_pay_req][list_payments[:
|
326
|
-
decoded = adapted[:decode_pay_req][list_payments[:
|
327
|
-
|
343
|
+
if adapted[:decode_pay_req][list_payments[:invoice][:code]]
|
344
|
+
decoded = adapted[:decode_pay_req][list_payments[:invoice][:code]]
|
345
|
+
invoice = list_payments[:invoice]
|
328
346
|
|
329
347
|
decoded.each_key do |key|
|
330
|
-
|
348
|
+
if !decoded[key].is_a?(Hash)
|
349
|
+
invoice[key] = decoded[key]
|
350
|
+
elsif decoded[key].is_a?(Hash)
|
351
|
+
invoice[key] = {} unless invoice.key?(key)
|
352
|
+
|
353
|
+
next if key == :secret
|
331
354
|
|
332
|
-
|
355
|
+
decoded[key].each_key do |sub_key|
|
356
|
+
next if decoded[key][sub_key].nil? ||
|
357
|
+
(decoded[key][sub_key].is_a?(String) && decoded[key][sub_key].empty?)
|
333
358
|
|
334
|
-
|
335
|
-
|
359
|
+
invoice[key][sub_key] = decoded[key][sub_key]
|
360
|
+
end
|
336
361
|
end
|
337
362
|
end
|
338
363
|
end
|
339
364
|
|
365
|
+
if list_payments[:invoice][:code]
|
366
|
+
if list_payments[:invoice][:payable] == 'once'
|
367
|
+
list_payments[:through] = 'non-amp'
|
368
|
+
elsif list_payments[:invoice][:payable] == 'indefinitely'
|
369
|
+
list_payments[:through] = 'amp'
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
340
373
|
list_payments
|
341
374
|
end
|
342
375
|
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../invoice/all'
|
4
|
+
require_relative '../../models/transaction'
|
5
|
+
|
6
|
+
module Lighstorm
|
7
|
+
module Controllers
|
8
|
+
module Transaction
|
9
|
+
module All
|
10
|
+
def self.fetch(limit: nil)
|
11
|
+
transactions = []
|
12
|
+
|
13
|
+
Invoice::All.data(spontaneous: true).filter do |invoice|
|
14
|
+
!invoice[:payments].nil? && invoice[:payments].size.positive?
|
15
|
+
end.each do |invoice|
|
16
|
+
invoice[:payments].each do |payment|
|
17
|
+
transactions << {
|
18
|
+
direction: 'in',
|
19
|
+
at: payment[:at],
|
20
|
+
amount: payment[:amount],
|
21
|
+
message: payment[:message],
|
22
|
+
kind: 'invoice',
|
23
|
+
data: invoice
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
transactions = transactions.sort_by { |transaction| -transaction[:at].to_i }
|
29
|
+
|
30
|
+
transactions = transactions[0..limit - 1] unless limit.nil?
|
31
|
+
|
32
|
+
{ transactions: transactions }
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.transform(raw)
|
36
|
+
raw[:transactions].map do |transaction|
|
37
|
+
transaction[:_key] = SecureRandom.hex
|
38
|
+
transaction
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.data(limit: nil, &vcr)
|
43
|
+
raw = vcr.nil? ? fetch(limit: limit) : vcr.call(-> { fetch(limit: limit) })
|
44
|
+
|
45
|
+
transform(raw)
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.model(data)
|
49
|
+
data.map { |data| Models::Transaction.new(data) }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/deleted.sh
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
deleted.sh
|