trolley 0.2.14 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/lib/trolley/Balance.rb +4 -0
  3. data/lib/trolley/Batch.rb +20 -0
  4. data/lib/trolley/BatchSummary.rb +21 -0
  5. data/lib/trolley/Client.rb +117 -0
  6. data/lib/trolley/Configuration.rb +30 -0
  7. data/lib/trolley/Exceptions.rb +26 -0
  8. data/lib/trolley/Gateway.rb +18 -0
  9. data/lib/trolley/Invoice.rb +22 -0
  10. data/lib/trolley/InvoicePayment.rb +13 -0
  11. data/lib/trolley/OfflinePayment.rb +23 -0
  12. data/lib/trolley/Payment.rb +52 -0
  13. data/lib/trolley/Recipient.rb +44 -0
  14. data/lib/trolley/RecipientAccount.rb +30 -0
  15. data/lib/trolley/gateways/BalanceGateway.rb +15 -0
  16. data/lib/trolley/gateways/BatchGateway.rb +74 -0
  17. data/lib/trolley/gateways/InvoiceGateway.rb +57 -0
  18. data/lib/trolley/gateways/InvoicePaymentGateway.rb +37 -0
  19. data/lib/trolley/gateways/OfflinePaymentGateway.rb +44 -0
  20. data/lib/trolley/gateways/PaymentGateway.rb +42 -0
  21. data/lib/trolley/gateways/RecipientAccountGateway.rb +42 -0
  22. data/lib/trolley/gateways/RecipientGateway.rb +82 -0
  23. data/lib/trolley/utils/PaginatedArray.rb +25 -0
  24. data/lib/trolley/utils/ResponseMapper.rb +80 -0
  25. data/lib/trolley.rb +33 -5
  26. data/spec/integration/BatchTest.rb +126 -0
  27. data/spec/integration/InvoicePaymentTest.rb +92 -0
  28. data/spec/integration/InvoiceTest.rb +128 -0
  29. data/spec/integration/RecipientAccountTest.rb +48 -0
  30. data/spec/integration/RecipientTest.rb +159 -0
  31. data/spec/integration/helper.rb +19 -0
  32. data/spec/unit/ConfigurationTest.rb +47 -0
  33. data/spec/unit/PaginatedArrayTest.rb +23 -0
  34. data/spec/unit/ResponseMapperTest.rb +26 -0
  35. data/spec/unit/TrolleyTest.rb +15 -0
  36. data/trolley.gemspec +15 -12
  37. metadata +96 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8ba931e6ff7016ee325fd67c087b7e20bc762c54d8d6e8982abf63a7fb8d507c
4
- data.tar.gz: 62b9f604e9ecb631c8e83129cf70aed9c3a09142cb8a6ad9e4f79cb981256203
3
+ metadata.gz: 3f504a3a31e96981b402289ae237a76e71201ad16b6ebfd8b07f5b5060dd9df9
4
+ data.tar.gz: 72cb4638918bc4b95f9c47d79d341e6a2d793c4182cd02bcb4f068f2c63f2252
5
5
  SHA512:
6
- metadata.gz: e55a122666265822edac8d924cfbeeeda8c3beef272bc0f244fbd125885d4bec6998bfc8351c57e6628b57f1bae7cde1896519130a938453e6c51090cadbf97e
7
- data.tar.gz: '08bf65fcf30c78570d790a76401dca50df8ae87f72821cbef6f48363733505cc0fc606a4e0d8b108cade33f13e0a99330ecdd612b6c80a363057ef4081adabe9'
6
+ metadata.gz: '060097cc078f7deb5c70920481dc35c6c853b0c8099e6778e439e6481fe83a82f6f098b625834a8ff1f1db7e03e4814a4d77e015e0c4ba640145cf585288fa37'
7
+ data.tar.gz: 85ef20b00e8f551599e0c440a7517442f0d7e5d4431071a2b5edf6340253b286222d63c5953ff2bf06472317ed5ea2580d1d1f948bce0bc3474c17a710dd8663
@@ -0,0 +1,4 @@
1
+ module Trolley
2
+ class Balance
3
+ end
4
+ end
@@ -0,0 +1,20 @@
1
+ module Trolley
2
+ class Batch
3
+ attr_accessor(
4
+ :id,
5
+ :amount,
6
+ :completedAt,
7
+ :createdAt,
8
+ :currency,
9
+ :description,
10
+ :sentAt,
11
+ :status,
12
+ :totalPayments,
13
+ :updatedAt,
14
+ :quoteExpiredAt,
15
+ :payments,
16
+ :tags,
17
+ :coverFees
18
+ )
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ module Trolley
2
+ class BatchSummary
3
+ attr_accessor(
4
+ :id,
5
+ :amount,
6
+ :completedAt,
7
+ :createdAt,
8
+ :currency,
9
+ :description,
10
+ :sentAt,
11
+ :status,
12
+ :totalPayments,
13
+ :updatedAt,
14
+ :methods,
15
+ :detail,
16
+ :total,
17
+ :balances,
18
+ :accounts
19
+ )
20
+ end
21
+ end
@@ -0,0 +1,117 @@
1
+ # require_relative 'Exceptions.rb'
2
+ require 'digest'
3
+ require 'net/http'
4
+ require 'openssl'
5
+ require 'uri'
6
+ require 'json'
7
+ require "rubygems"
8
+
9
+ module Trolley
10
+ class Client
11
+ def initialize(config)
12
+ @config = config
13
+ end
14
+
15
+ def get(endPoint)
16
+ send_request(endPoint, 'GET')
17
+ end
18
+
19
+ def post(endPoint, body)
20
+ body = body.to_json
21
+ send_request(endPoint, 'POST', body)
22
+ end
23
+
24
+ def delete(endPoint, body = '')
25
+ body = body.to_json if body != ''
26
+ send_request(endPoint, 'DELETE', body)
27
+ end
28
+
29
+ def patch(endPoint, body)
30
+ body = body.to_json
31
+ send_request(endPoint, 'PATCH', body)
32
+ end
33
+
34
+ private
35
+
36
+ # rubocop:disable Metrics/CyclomaticComplexity
37
+ # rubocop:disable Metrics/PerceivedComplexity
38
+ def send_request(endPoint, method, body = '')
39
+ uri = URI.parse(@config.api_base + endPoint)
40
+ http = Net::HTTP.new(
41
+ uri.host, uri.port,
42
+ @config.proxy&.host, @config.proxy&.port, @config.proxy&.user, @config.proxy&.password
43
+ )
44
+ http.use_ssl = @config.useSsl?
45
+
46
+ time = Time.now.to_i
47
+ headers = {'X-PR-Timestamp': time.to_s,
48
+ 'Authorization': generate_authorization(time, endPoint, method, body),
49
+ 'Content-Type': 'application/json',
50
+ 'Trolley-Source': "ruby-sdk_#{::Trolley::VERSION}"}
51
+
52
+ if method === "GET"
53
+ request = Net::HTTP::Get.new(uri.request_uri, headers)
54
+ elsif method === "POST"
55
+ request = Net::HTTP::Post.new(uri.request_uri, headers)
56
+ request.body = body
57
+ elsif method === "PATCH"
58
+ request = Net::HTTP::Patch.new(uri.request_uri, headers)
59
+ request.body = body
60
+ elsif method === "DELETE"
61
+ request = Net::HTTP::Delete.new(uri.request_uri, headers)
62
+ request.body = body
63
+ end
64
+
65
+ response = http.request(request)
66
+
67
+ if response.code != '200' && response.code != '204'
68
+ throw_status_code_exception("#{response.message} #{response.body}" , response.code, response.body)
69
+ end
70
+ response.body
71
+ end
72
+ # rubocop:enable Metrics/CyclomaticComplexity
73
+ # rubocop:enable Metrics/PerceivedComplexity
74
+
75
+ private
76
+ def generate_authorization(time, endPoint, method, body)
77
+ message = "#{[time.to_s, method, endPoint, body].join("\n")}\n"
78
+ signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), @config.privateKey, message)
79
+ "prsign #{@config.publicKey}:#{signature}"
80
+ end
81
+
82
+ private
83
+
84
+ # rubocop:disable Metrics/CyclomaticComplexity
85
+ def throw_status_code_exception(message, code, body = nil)
86
+ validation_errors = []
87
+ unless body.nil?
88
+ begin
89
+ body = JSON.parse(body)
90
+ validation_errors = body['errors']
91
+ rescue JSON::ParserError
92
+ # no-op
93
+ end
94
+ end
95
+
96
+ case code
97
+ when '400'
98
+ raise MalformedRequestError.new(message, validation_errors)
99
+ when '401'
100
+ raise AuthenticationError.new(message, validation_errors)
101
+ when '403'
102
+ raise AuthorizationError.new(message, validation_errors)
103
+ when '404'
104
+ raise NotFoundError.new(message, validation_errors)
105
+ when '429'
106
+ raise TooManyRequestsError.new(message, validation_errors)
107
+ when '500'
108
+ raise ServerError.new(message, validation_errors)
109
+ when '503'
110
+ raise DownForMaintenanceError.new(message, validation_errors)
111
+ else
112
+ raise UnexpectedError.new(message, validation_errors)
113
+ end
114
+ end
115
+ # rubocop:enable Metrics/CyclomaticComplexity
116
+ end
117
+ end
@@ -0,0 +1,30 @@
1
+ module Trolley
2
+ class Configuration
3
+ class InvalidProxyAddress < StandardError; end
4
+ attr_reader :api_base, :proxy, :environment
5
+
6
+ DEFAULT_API_BASE = 'https://api.trolley.com'.freeze
7
+
8
+ def initialize(publicKey, privateKey, **optionals)
9
+ raise ArgumentError, 'Both key/secret must be a nonempty string' if publicKey.to_s&.empty? || privateKey.to_s&.empty?
10
+
11
+ @publicKey = publicKey
12
+ @privateKey = privateKey
13
+ @api_base = optionals[:api_base] || DEFAULT_API_BASE
14
+ # failfast on a bad proxy
15
+ proxy_uri = optionals[:proxy_uri]
16
+ begin
17
+ @proxy = proxy_uri.nil? ? nil : URI.parse(proxy_uri)
18
+ rescue URI::InvalidURIError
19
+ raise InvalidProxyAddress, "Invalid proxy provided to configuration: #{proxy_uri}"
20
+ end
21
+ end
22
+
23
+ def useSsl?
24
+ api_base.start_with? 'https'
25
+ end
26
+
27
+ attr_accessor :publicKey, :privateKey
28
+
29
+ end
30
+ end
@@ -0,0 +1,26 @@
1
+ module Trolley
2
+ class TrolleyError < ::StandardError
3
+ attr_reader :validation_errors
4
+
5
+ def initialize(message, validation_errors)
6
+ super(message)
7
+ @validation_errors = validation_errors
8
+ end
9
+ end
10
+
11
+ class AuthenticationError < TrolleyError; end
12
+
13
+ class AuthorizationError < TrolleyError; end
14
+
15
+ class DownForMaintenanceError < TrolleyError; end
16
+
17
+ class NotFoundError < TrolleyError; end
18
+
19
+ class ServerError < TrolleyError; end
20
+
21
+ class TooManyRequestsError < TrolleyError; end
22
+
23
+ class UnexpectedError < TrolleyError; end
24
+
25
+ class MalformedRequestError < TrolleyError; end
26
+ end
@@ -0,0 +1,18 @@
1
+ module Trolley
2
+ class Gateway
3
+ attr_accessor :config, :client, :recipient, :recipient_account, :batch, :payment, :balance, :offline_payment, :invoice, :invoice_payment
4
+
5
+ def initialize(config)
6
+ @config = config
7
+ @client = Client.new(config)
8
+ @recipient = RecipientGateway.new(client)
9
+ @recipient_account = RecipientAccountGateway.new(client)
10
+ @batch = BatchGateway.new(client)
11
+ @payment = PaymentGateway.new(client)
12
+ @balance = BalanceGateway.new(client)
13
+ @offline_payment = OfflinePaymentGateway.new(client)
14
+ @invoice = InvoiceGateway.new(client)
15
+ @invoice_payment = InvoicePaymentGateway.new(client)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,22 @@
1
+ module Trolley
2
+ class Invoice
3
+ attr_accessor(
4
+ :id,
5
+ :description,
6
+ :externalId,
7
+ :invoiceDate,
8
+ :dueDate,
9
+ :invoiceNumber,
10
+ :status,
11
+ :releaseAfter,
12
+ :createdAt,
13
+ :updatedAt,
14
+ :totalAmount,
15
+ :paidAmount,
16
+ :dueAmount,
17
+ :tags,
18
+ :recipientId,
19
+ :lines
20
+ )
21
+ end
22
+ end
@@ -0,0 +1,13 @@
1
+ module Trolley
2
+ class InvoicePayment
3
+ attr_accessor(
4
+ :id,
5
+ :batchId,
6
+ :paymentId,
7
+ :invoiceId,
8
+ :invoiceLineId,
9
+ :amount,
10
+ :invoicePayments
11
+ )
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ module Trolley
2
+ class OfflinePayment
3
+ attr_accessor(
4
+ :id,
5
+ :recipientId,
6
+ :externalId,
7
+ :memo,
8
+ :tags,
9
+ :taxReportable,
10
+ :category,
11
+ :amount,
12
+ :currency,
13
+ :withholdingAmount,
14
+ :withholdingCurrency,
15
+ :processedAt,
16
+ :equivalentWithholdingAmount,
17
+ :equivalentWithholdingCurrency,
18
+ :updatedAt,
19
+ :createdAt,
20
+ :deletedAt
21
+ )
22
+ end
23
+ end
@@ -0,0 +1,52 @@
1
+ module Trolley
2
+ class Payment
3
+ attr_accessor(
4
+ :id,
5
+ :status,
6
+ :isSupplyPayment,
7
+ :returnedAmount,
8
+ :sourceAmount,
9
+ :sourceCurrency,
10
+ :targetAmount,
11
+ :targetCurrency,
12
+ :exchangeRate,
13
+ :fees,
14
+ :recipientFees,
15
+ :fxRate,
16
+ :memo,
17
+ :externalId,
18
+ :processedAt,
19
+ :createdAt,
20
+ :updatedAt,
21
+ :merchantFees,
22
+ :compliance,
23
+ :payoutMethod,
24
+ :estimatedDeliveryAt,
25
+ :recipient,
26
+ :withholdingAmount,
27
+ :withholdingCurrency,
28
+ :equivalentWithholdingAmount,
29
+ :equivalentWithholdingCurrency,
30
+ :methodDisplay,
31
+ :batch,
32
+ :coverFees,
33
+ :category,
34
+ :amount,
35
+ :currency,
36
+ :taxReportable,
37
+ :taxBasisAmount,
38
+ :taxBasisCurrency,
39
+ :tags,
40
+ :account,
41
+ :initiatedAt,
42
+ :settledAt,
43
+ :returnedAt,
44
+ :returnedNote,
45
+ :returnedReason,
46
+ :failureMessage,
47
+ :merchantId,
48
+ :checkNumber,
49
+ :forceUsTaxActivity
50
+ )
51
+ end
52
+ end
@@ -0,0 +1,44 @@
1
+ module Trolley
2
+ class Recipient
3
+ attr_accessor(
4
+ :id,
5
+ :routeMinimum,
6
+ :routeType,
7
+ :estimatedFees,
8
+ :referenceId,
9
+ :email,
10
+ :name,
11
+ :lastName,
12
+ :firstName,
13
+ :type,
14
+ :taxType,
15
+ :status,
16
+ :language,
17
+ :complianceStatus,
18
+ :dob,
19
+ :contactEmails,
20
+ :passport,
21
+ :updatedAt,
22
+ :createdAt,
23
+ :gravatarUrl,
24
+ :governmentId,
25
+ :ssn,
26
+ :primaryCurrency,
27
+ :merchantId,
28
+ :payout,
29
+ :compliance,
30
+ :accounts,
31
+ :address,
32
+ :taxWithholdingPercentage,
33
+ :taxForm,
34
+ :taxFormStatus,
35
+ :inactiveReasons,
36
+ :payoutMethod,
37
+ :placeOfBirth,
38
+ :tags,
39
+ :taxDeliveryType,
40
+ :riskScore,
41
+ :isPortalUser
42
+ )
43
+ end
44
+ end
@@ -0,0 +1,30 @@
1
+ module Trolley
2
+ class RecipientAccount
3
+ attr_accessor(
4
+ :id,
5
+ :primary,
6
+ :currency,
7
+ :recipientAccountId,
8
+ :recipientId,
9
+ :recipientReferenceId,
10
+ :routeType,
11
+ :recipientFees,
12
+ :emailAddress,
13
+ :country,
14
+ :type,
15
+ :iban,
16
+ :accountNum,
17
+ :accountHolderName,
18
+ :swiftBic,
19
+ :branchId,
20
+ :bankId,
21
+ :bankName,
22
+ :bankAddress,
23
+ :bankCity,
24
+ :bankRegionCode,
25
+ :bankPostalCode,
26
+ :status,
27
+ :disabledAt
28
+ )
29
+ end
30
+ end
@@ -0,0 +1,15 @@
1
+ require_relative '../Client'
2
+
3
+ module Trolley
4
+ class BalanceGateway
5
+ def initialize(client)
6
+ @client = client
7
+ end
8
+
9
+ def find(term = '')
10
+ response = @client.get("/v1/balances/#{term}" )
11
+ JSON.parse(response, object_class: OpenStruct)
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,74 @@
1
+ require_relative '../Client'
2
+
3
+ module Trolley
4
+ class BatchGateway
5
+ def initialize(client)
6
+ @client = client
7
+ end
8
+
9
+ def find(batch_id)
10
+ response = @client.get("/v1/batches/#{batch_id}")
11
+ batch_builder(response)
12
+ end
13
+
14
+ def all
15
+ response = @client.get('/v1/batches/')
16
+ batch_list_builder(response)
17
+ end
18
+
19
+ def create(body)
20
+ response = @client.post('/v1/batches/', body)
21
+ batch_builder(response)
22
+ end
23
+
24
+ def update(batch_id, body)
25
+ @client.patch("/v1/batches/#{batch_id}", body)
26
+ true
27
+ end
28
+
29
+ # @param batch_id [String] or [Array] The id (or array of ids) of the batch to delete
30
+ def delete(batch_id)
31
+ path = '/v1/batches/'
32
+ body = ''
33
+
34
+ if batch_id.is_a?(String)
35
+ path += batch_id
36
+ elsif batch_id.is_a?(Array)
37
+ body = { ids: batch_id }
38
+ else
39
+ raise 'Invalid batch_id type, please pass a string or array of strings'
40
+ end
41
+
42
+ @client.delete(path, body)
43
+ true
44
+ end
45
+
46
+ def generate_quote(batch_id)
47
+ response = @client.post("/v1/batches/#{batch_id}/generate-quote", {})
48
+ batch_builder(response)
49
+ end
50
+
51
+ def start_processing(batch_id)
52
+ response = @client.post("/v1/batches/#{batch_id}/start-processing", {})
53
+ batch_builder(response)
54
+ end
55
+
56
+ def search(page = 1, page_size = 10, term = '')
57
+ response = @client.get("/v1/batches?page=#{page}&pageSize=#{page_size}&search=#{term}")
58
+ batch_list_builder(response)
59
+ end
60
+
61
+ def batch_builder(response)
62
+ Utils::ResponseMapper.build(response, Batch)
63
+ end
64
+
65
+ def summary(batch_id)
66
+ response = @client.get("/v1/batches/#{batch_id}/summary")
67
+ Utils::ResponseMapper.build(response, BatchSummary)
68
+ end
69
+
70
+ def batch_list_builder(response)
71
+ Utils::PaginatedArray.from_response(response, Batch)
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,57 @@
1
+ require_relative '../Client'
2
+
3
+ module Trolley
4
+ class InvoiceGateway
5
+ def initialize(client)
6
+ @client = client
7
+ end
8
+
9
+ def find(body)
10
+ response = @client.post('/v1/invoices/get', body)
11
+ invoice_builder(response)
12
+ end
13
+
14
+ def create(body)
15
+ response = @client.post('/v1/invoices/create', body)
16
+ invoice_builder(response)
17
+ end
18
+
19
+ def create_line(body)
20
+ response = @client.post('/v1/invoices/create-lines', body)
21
+ invoice_builder(response)
22
+ end
23
+
24
+ def search(body)
25
+ response = @client.post('/v1/invoices/search', body)
26
+ invoice_list_builder(response)
27
+ end
28
+
29
+ def update(body)
30
+ @client.post('/v1/invoices/update', body)
31
+ true
32
+ end
33
+
34
+ def update_line(body)
35
+ @client.post('/v1/invoices/update-lines', body)
36
+ true
37
+ end
38
+
39
+ def delete(body)
40
+ @client.post('/v1/invoices/delete', body)
41
+ true
42
+ end
43
+
44
+ def delete_line(body)
45
+ @client.post('/v1/invoices/delete-lines', body)
46
+ true
47
+ end
48
+
49
+ def invoice_builder(response)
50
+ Utils::ResponseMapper.build(response, Invoice)
51
+ end
52
+
53
+ def invoice_list_builder(response)
54
+ Utils::PaginatedArray.from_response(response, Invoice)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,37 @@
1
+ require_relative '../Client'
2
+
3
+ module Trolley
4
+ class InvoicePaymentGateway
5
+ def initialize(client)
6
+ @client = client
7
+ end
8
+
9
+ def create(body)
10
+ response = @client.post('/v1/invoices/payment/create', body)
11
+ invoice_payment_builder(response)
12
+ end
13
+
14
+ def update(body)
15
+ @client.post('/v1/invoices/payment/update', body)
16
+ true
17
+ end
18
+
19
+ def delete(body)
20
+ @client.post('/v1/invoices/payment/delete', body)
21
+ true
22
+ end
23
+
24
+ def search(body)
25
+ response = @client.post('/v1/invoices/payment/search', body)
26
+ invoice_payments_list_builder(response)
27
+ end
28
+
29
+ def invoice_payment_builder(response)
30
+ Utils::ResponseMapper.build(response, InvoicePayment)
31
+ end
32
+
33
+ def invoice_payments_list_builder(response)
34
+ Utils::PaginatedArray.from_response(response, InvoicePayment)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,44 @@
1
+ require_relative '../Client'
2
+
3
+ module Trolley
4
+ class OfflinePaymentGateway
5
+ def initialize(client)
6
+ @client = client
7
+ end
8
+
9
+ def create(recipient_id, body)
10
+ response = @client.post("/v1/recipients/#{recipient_id}/offlinePayments", body)
11
+ offline_payment_builder(response)
12
+ end
13
+
14
+ def update(recipient_id, offline_payment_id, body)
15
+ @client.patch("/v1/recipients/#{recipient_id}/offlinePayments/#{offline_payment_id}", body)
16
+ true
17
+ end
18
+
19
+ def delete(recipient_id, offline_payment_id)
20
+ @client.delete("/v1/recipients/#{recipient_id}/offlinePayments/#{offline_payment_id}")
21
+ true
22
+ end
23
+
24
+ # rubocop:disable Metrics/ParameterLists
25
+ def search(recipient_id = '', page = 1, page_size = 10, term = '')
26
+ if recipient_id === ''
27
+ response = @client.get("/v1/offline-payments?page=#{page}&pageSize=#{page_size}&search=#{term}")
28
+ else
29
+ response = @client.get("/v1/recipients/#{recipient_id}/offlinePayments?page=#{page}&pageSize=#{page_size}&search=#{term}")
30
+ end
31
+
32
+ offline_payments_list_builder(response)
33
+ end
34
+ # rubocop:enable Metrics/ParameterLists
35
+
36
+ def offline_payment_builder(response)
37
+ Utils::ResponseMapper.build(response, OfflinePayment)
38
+ end
39
+
40
+ def offline_payments_list_builder(response)
41
+ Utils::PaginatedArray.from_response(response, OfflinePayment)
42
+ end
43
+ end
44
+ end