pensio_api 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +13 -5
  2. data/.rspec +2 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +83 -0
  5. data/Guardfile +23 -0
  6. data/LICENSE +24 -0
  7. data/README.md +77 -0
  8. data/lib/pensio_api/billing_address.rb +15 -0
  9. data/lib/pensio_api/callback.rb +30 -0
  10. data/lib/pensio_api/credentials.rb +7 -0
  11. data/lib/pensio_api/ecommerce.rb +13 -0
  12. data/lib/pensio_api/errors/bad_request.rb +15 -0
  13. data/lib/pensio_api/errors/gateway_error.rb +21 -0
  14. data/lib/pensio_api/errors/no_credentials.rb +9 -0
  15. data/lib/pensio_api/funding_list.rb +30 -0
  16. data/lib/pensio_api/funding_list_request.rb +15 -0
  17. data/lib/pensio_api/mixins/has_transactions.rb +36 -0
  18. data/lib/pensio_api/mixins/id.rb +9 -0
  19. data/lib/pensio_api/mixins/method_missing.rb +9 -0
  20. data/lib/pensio_api/mixins/request_defaults.rb +50 -0
  21. data/lib/pensio_api/mixins/timestamps.rb +13 -0
  22. data/lib/pensio_api/request.rb +18 -0
  23. data/lib/pensio_api/reservation.rb +28 -0
  24. data/lib/pensio_api/responses/base.rb +32 -0
  25. data/lib/pensio_api/responses/funding_list.rb +43 -0
  26. data/lib/pensio_api/responses/gateway_url.rb +13 -0
  27. data/lib/pensio_api/responses/refund.rb +17 -0
  28. data/lib/pensio_api/responses/reservation.rb +15 -0
  29. data/lib/pensio_api/responses/reservation_capture.rb +14 -0
  30. data/lib/pensio_api/responses/reservation_release.rb +6 -0
  31. data/lib/pensio_api/responses/subscription_charge.rb +21 -0
  32. data/lib/pensio_api/responses/subscription_failure_callback.rb +21 -0
  33. data/lib/pensio_api/responses/success_callback.rb +21 -0
  34. data/lib/pensio_api/responses/terminal.rb +41 -0
  35. data/lib/pensio_api/responses/transaction.rb +13 -0
  36. data/lib/pensio_api/subscription.rb +30 -0
  37. data/lib/pensio_api/terminal.rb +17 -0
  38. data/lib/pensio_api/transaction.rb +84 -0
  39. data/lib/pensio_api.rb +36 -0
  40. data/pensio_api.gemspec +20 -0
  41. data/spec/lib/pensio_api/billing_address_spec.rb +40 -0
  42. data/spec/lib/pensio_api/callback_spec.rb +21 -0
  43. data/spec/lib/pensio_api/ecommerce_spec.rb +22 -0
  44. data/spec/lib/pensio_api/errors/bad_request_spec.rb +31 -0
  45. data/spec/lib/pensio_api/errors/gateway_error_spec.rb +38 -0
  46. data/spec/lib/pensio_api/funding_list_request_spec.rb +17 -0
  47. data/spec/lib/pensio_api/funding_list_spec.rb +38 -0
  48. data/spec/lib/pensio_api/request_spec.rb +122 -0
  49. data/spec/lib/pensio_api/reservation_spec.rb +32 -0
  50. data/spec/lib/pensio_api/responses/base_spec.rb +68 -0
  51. data/spec/lib/pensio_api/responses/funding_list_spec.rb +23 -0
  52. data/spec/lib/pensio_api/responses/gateway_url_spec.rb +17 -0
  53. data/spec/lib/pensio_api/responses/refund_spec.rb +18 -0
  54. data/spec/lib/pensio_api/responses/reservation_capture_spec.rb +18 -0
  55. data/spec/lib/pensio_api/responses/reservation_release_spec.rb +12 -0
  56. data/spec/lib/pensio_api/responses/reservation_spec.rb +15 -0
  57. data/spec/lib/pensio_api/responses/subscription_charge_spec.rb +56 -0
  58. data/spec/lib/pensio_api/responses/subscription_failure_callback_spec.rb +48 -0
  59. data/spec/lib/pensio_api/responses/success_callback_spec.rb +45 -0
  60. data/spec/lib/pensio_api/responses/terminal_spec.rb +55 -0
  61. data/spec/lib/pensio_api/responses/transaction_spec.rb +56 -0
  62. data/spec/lib/pensio_api/subscription_spec.rb +37 -0
  63. data/spec/lib/pensio_api/terminal_spec.rb +18 -0
  64. data/spec/lib/pensio_api/transaction_spec.rb +101 -0
  65. data/spec/spec_helper.rb +26 -0
  66. data/spec/support/fixtures/bad_request_error.xml +10 -0
  67. data/spec/support/fixtures/capture_reservation.xml +84 -0
  68. data/spec/support/fixtures/charge_subscription.xml +89 -0
  69. data/spec/support/fixtures/create_multi_payment_request.xml +13 -0
  70. data/spec/support/fixtures/create_payment_request.xml +13 -0
  71. data/spec/support/fixtures/failure_callback.xml +76 -0
  72. data/spec/support/fixtures/funding_download.csv +6 -0
  73. data/spec/support/fixtures/funding_list.xml +43 -0
  74. data/spec/support/fixtures/get_terminals.xml +38 -0
  75. data/spec/support/fixtures/get_terminals_none.xml +12 -0
  76. data/spec/support/fixtures/get_terminals_single.xml +28 -0
  77. data/spec/support/fixtures/multiple_payments.xml +146 -0
  78. data/spec/support/fixtures/payments.xml +80 -0
  79. data/spec/support/fixtures/payments_none.xml +13 -0
  80. data/spec/support/fixtures/pensio_error.xml +48 -0
  81. data/spec/support/fixtures/refund_captured_reservation.xml +88 -0
  82. data/spec/support/fixtures/release_reservation.xml +72 -0
  83. data/spec/support/fixtures/reservation_of_fixed_amount.xml +46 -0
  84. data/spec/support/fixtures/reserve_subscription_charge.xml +86 -0
  85. data/spec/support/fixtures/setup_subscription.xml +46 -0
  86. data/spec/support/fixtures/subscription_failure_callback.xml +132 -0
  87. data/spec/support/fixtures/success_callback.xml +119 -0
  88. data/spec/support/helpers.rb +38 -0
  89. metadata +150 -15
checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 689ae01e09472f1266a2da94b30861b7bda91871
4
- data.tar.gz: 8c73eccf3325acbddd6c7eb258bc4ef2ec500196
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MmJkMDI1NTg4NzNiYjk4NjQ1ZTFjMGY5YTAxOGMxM2Y1MTgwMWZkZg==
5
+ data.tar.gz: !binary |-
6
+ NTEzODc3OWY4Yjc2YjA1NDk0ODQ1MGU2MzE3NTQ1OWI0MThmZTA0NQ==
5
7
  SHA512:
6
- metadata.gz: 85db0f900db0c6a16e6d0a1fbd9175fdd394d1c7e6a4ca2af78bd2b0e17ead99fd0607e2912948ea7cc6d0e21d0cbb1510ded4cc41dc32a15a4734e5a85088e5
7
- data.tar.gz: fff1cdfe9534a05e774d332db04919f069d832d50ec65614d6dc808f136d193015401ce432741c35bff17d0f292668844a70709dc7c1129114f1f3ef0d888529
8
+ metadata.gz: !binary |-
9
+ N2U3YjYwMjZjYWVlN2UyNTczYmNiZTZhMzE1N2JjYzgwMzAwMmYwZmNlOThh
10
+ NjM3YzdhOTMwZmVkYTBlMDFiODExNzEzYzNmMzgxMDNmZWU0OTc4MGU4NWI3
11
+ OTkwOWFkNjVkN2RjMzNhNDkyNjE2ZWM5ODdiYTgxNGEzMjJkYjk=
12
+ data.tar.gz: !binary |-
13
+ ODRiYzQzY2JmODhkYmFhMjE4ZTdhZjQzMWJhMzU2ZmRhMGRhNTMwMjgzMTc0
14
+ NTBlNWE0ZDVlNmE0NDIzNjdlNDE4ODQzMDAwZDBiNTMyNjkxOWNhMjAyMmM0
15
+ NjBlNzUwMjY0ZDdiMTk0NjhhZDc1NDQxMmRmZjAzZTJmMGE2YjE=
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://www.rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,83 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ pensio_api (0.2.2)
5
+ activesupport (>= 3.2)
6
+ httparty (>= 0.12.0)
7
+ multi_xml (>= 0.5.2)
8
+
9
+ GEM
10
+ remote: https://www.rubygems.org/
11
+ specs:
12
+ activesupport (4.0.4)
13
+ i18n (~> 0.6, >= 0.6.9)
14
+ minitest (~> 4.2)
15
+ multi_json (~> 1.3)
16
+ thread_safe (~> 0.1)
17
+ tzinfo (~> 0.3.37)
18
+ addressable (2.3.5)
19
+ celluloid (0.15.2)
20
+ timers (~> 1.1.0)
21
+ coderay (1.1.0)
22
+ crack (0.4.1)
23
+ safe_yaml (~> 0.9.0)
24
+ diff-lcs (1.2.5)
25
+ ffi (1.9.3)
26
+ formatador (0.2.4)
27
+ guard (2.2.5)
28
+ formatador (>= 0.2.4)
29
+ listen (~> 2.1)
30
+ lumberjack (~> 1.0)
31
+ pry (>= 0.9.12)
32
+ thor (>= 0.18.1)
33
+ guard-rspec (4.2.4)
34
+ guard (~> 2.1)
35
+ rspec (>= 2.14, < 4.0)
36
+ httparty (0.13.0)
37
+ json (~> 1.8)
38
+ multi_xml (>= 0.5.2)
39
+ i18n (0.6.9)
40
+ json (1.8.1)
41
+ listen (2.4.0)
42
+ celluloid (>= 0.15.2)
43
+ rb-fsevent (>= 0.9.3)
44
+ rb-inotify (>= 0.9)
45
+ lumberjack (1.0.4)
46
+ method_source (0.8.2)
47
+ minitest (4.7.5)
48
+ multi_json (1.9.2)
49
+ multi_xml (0.5.5)
50
+ pry (0.9.12.4)
51
+ coderay (~> 1.0)
52
+ method_source (~> 0.8)
53
+ slop (~> 3.4)
54
+ rb-fsevent (0.9.4)
55
+ rb-inotify (0.9.3)
56
+ ffi (>= 0.5.0)
57
+ rspec (2.14.1)
58
+ rspec-core (~> 2.14.0)
59
+ rspec-expectations (~> 2.14.0)
60
+ rspec-mocks (~> 2.14.0)
61
+ rspec-core (2.14.7)
62
+ rspec-expectations (2.14.4)
63
+ diff-lcs (>= 1.1.3, < 2.0)
64
+ rspec-mocks (2.14.4)
65
+ safe_yaml (0.9.7)
66
+ slop (3.4.7)
67
+ thor (0.18.1)
68
+ thread_safe (0.3.2)
69
+ timers (1.1.0)
70
+ tzinfo (0.3.39)
71
+ webmock (1.16.1)
72
+ addressable (>= 2.2.7)
73
+ crack (>= 0.3.2)
74
+
75
+ PLATFORMS
76
+ ruby
77
+
78
+ DEPENDENCIES
79
+ guard (~> 2.2.5)
80
+ guard-rspec (~> 4.2.4)
81
+ pensio_api!
82
+ rspec (>= 2.14)
83
+ webmock (~> 1.16.1)
data/Guardfile ADDED
@@ -0,0 +1,23 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :rspec do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+
9
+ # Rails example
10
+ watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
11
+ watch(%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
12
+ watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
13
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
14
+ watch('config/routes.rb') { "spec/routing" }
15
+ watch('app/controllers/application_controller.rb') { "spec/controllers" }
16
+
17
+ # Capybara features specs
18
+ watch(%r{^app/views/(.+)/.*\.(erb|haml|slim)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
19
+
20
+ # Turnip features and steps
21
+ watch(%r{^spec/acceptance/(.+)\.feature$})
22
+ watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
23
+ end
data/LICENSE ADDED
@@ -0,0 +1,24 @@
1
+ Copyright (c) 2014, ASMALLWORLD, AG
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+ * Redistributions of source code must retain the above copyright
7
+ notice, this list of conditions and the following disclaimer.
8
+ * Redistributions in binary form must reproduce the above copyright
9
+ notice, this list of conditions and the following disclaimer in the
10
+ documentation and/or other materials provided with the distribution.
11
+ * Neither the name of ASMALLWORLD, AG nor the
12
+ names of its contributors may be used to endorse or promote products
13
+ derived from this software without specific prior written permission.
14
+
15
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ DISCLAIMED. IN NO EVENT SHALL ASMALLWORLD, AG BE LIABLE FOR ANY
19
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # PensioAPI
2
+ This gem covers most of the Merchant and eCommerce API functionality provided by Pensio (http://www.pensio.com).
3
+
4
+ ## Getting Started
5
+
6
+ First, register your Pensio credentials. For example, if you're using rails, create `config/initializers/pensio.rb` with the following contents:
7
+
8
+ ```
9
+ PensioAPI::Credentials.base_uri = 'Your pensio gateway URI'
10
+ PensioAPI::Credentials.username = 'Your pensio username'
11
+ PensioAPI::Credentials.password = 'Your pensio password'
12
+ ```
13
+
14
+ ## Transactions
15
+
16
+ To query transactions in your Pensio gateway, use the method `PensioAPI::Transaction.find`. This takes a number of parameters as defined in the Pensio documentation. For example:
17
+
18
+ ```
19
+ PensioAPI::Transaction.find(transaction_id: '123')
20
+ ```
21
+
22
+ This returns an enumerable `PensioAPI::Responses::Transaction` object as many `PensioAPI::Transaction` objects as match your criteria.
23
+
24
+ With a transaction object, you can perform several actions.
25
+
26
+ * Determine status via `.status`, `.captured?` and `.reserved`
27
+ * Refund via `.refund`
28
+ * Expose terminal details via `.terminal`
29
+ * Expose billing address via `.billing_address`
30
+ * Map to a subscription object via `.to_subscription`
31
+ * Map to a reservation object via `.to_reservation`
32
+
33
+ ## Funding Lists
34
+
35
+ The funding list functionality of the Pensio Merchant API is exposed via `PensioAPI::FundingList.all` which returns an iterable collection of `PensioAPI::FundingList` objects. Calling `.download` on any of these objects downloads and parses the CSV file from the Pensio Gateway.
36
+
37
+ ## Terminals
38
+
39
+ To query terminals, use the method `PensioAPI::Terminal.all`. This will return an enumerable object containing as many `PensioAPI::Terminals` as are associated with your gateway.
40
+
41
+ ## Reservations
42
+
43
+ To create a fixed amount reservation (via the API call '/merchant/API/reservationOfFixedAmount') use the class method `PensioAPI::Reservation.of_fixed_amount`.
44
+
45
+ To capture an existing reservation, use `.capture`. Likewise to release it, use `.release`.
46
+
47
+ ## Subscriptions
48
+
49
+ To create a subscription, use the class method `PensioAPI::Subscription.setup`. This takes the arguments documented in Pensio's documentation for the '/merchant/API/setupSubscription' API call.
50
+
51
+ With an existing subscription, use `.reserve_charge` to place a charge reservation upon the subscription or `.charge` to attempt a reservation and charge of the associated transaction.
52
+
53
+ ## eCommerce
54
+
55
+ PensioAPI::Ecommerce is a module which exposes the eCommerce API endpoints. Two module methods `.create_payment_request` and `.create_multi_payment_request` can be used to generate payment URLs for a multitude of payment types. Consult the Pensio eCommerce API documentation for parameter details.
56
+
57
+ ## Callbacks
58
+
59
+ `PensioAPI::Callback` provides two methods `.parse_success` and `.parse_failure`. These are designed to parse the callbacks Pensio send to your transaction success and transaction failure endpoints. Each returns a callback response which exposes the transaction details for processing by your app.
60
+
61
+ ## Error Handling
62
+
63
+ The library provides two generic error classes for requests.
64
+
65
+ * `PensioAPI::Errors::BadRequest` is raised when the response from Pensio indicates that a request has invalid parameters
66
+ * `PensioAPI::Errors::GatewayError` is raised when the parameters have been provided correctly, but the gateway was unable to process the request
67
+
68
+ Additionally, a third error class `PensioAPI::NoCredentials` is raised if credentials have not been provided. See "Getting Started" for more details.
69
+
70
+ ## TODO
71
+
72
+ * Better documentation - more examples, RDoc docs
73
+ * Further API implementation
74
+
75
+ ## Contributing
76
+
77
+ Contributions are very welcome. Please submit pull requests with adequately-tested code.
@@ -0,0 +1,15 @@
1
+ module PensioAPI
2
+ class BillingAddress
3
+ attr_reader :first_name, :last_name, :street_address, :city, :region, :postal_code, :country
4
+
5
+ def initialize(address_params)
6
+ @first_name = address_params['Firstname']
7
+ @last_name = address_params['Lastname']
8
+ @street_address = address_params['Address']
9
+ @city = address_params['City']
10
+ @region = address_params['Region']
11
+ @postal_code = address_params['PostalCode']
12
+ @country = address_params['Country']
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,30 @@
1
+ module PensioAPI
2
+ module Callback
3
+ FakeRequest = Struct.new(:headers, :body)
4
+
5
+ def self.parse_success(xml)
6
+ parse(xml, true)
7
+ end
8
+
9
+ def self.parse_failure(xml)
10
+ parse(xml, false)
11
+ end
12
+
13
+ private
14
+
15
+ def self.parse(xml, success)
16
+ params = MultiXml.parse(xml)
17
+
18
+ request = FakeRequest.new(
19
+ params['APIResponse']['Header'],
20
+ params['APIResponse']['Body']
21
+ )
22
+
23
+ if success
24
+ PensioAPI::Responses::SuccessCallback.new(request)
25
+ else
26
+ PensioAPI::Responses::SubscriptionFailureCallback.new(request)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,7 @@
1
+ module PensioAPI
2
+ module Credentials
3
+ class << self
4
+ attr_accessor :base_uri, :username, :password
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ module PensioAPI
2
+ module Ecommerce
3
+ def self.create_payment_request(options={})
4
+ request = Request.new('/merchant/API/createPaymentRequest', options)
5
+ Responses::GatewayURL.new(request)
6
+ end
7
+
8
+ def self.create_multi_payment_request(options={})
9
+ request = Request.new('/merchant/API/createMultiPaymentRequest', options)
10
+ Responses::GatewayURL.new(request)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,15 @@
1
+ module PensioAPI
2
+ module Errors
3
+ class BadRequest < StandardError
4
+ attr_reader :request_time, :request_path, :error_code
5
+
6
+ def initialize(request)
7
+ super(request.headers['ErrorMessage'])
8
+
9
+ @request_time = Time.parse(request.headers['Date'])
10
+ @request_path = request.headers['Path']
11
+ @error_code = request.headers['ErrorCode'].to_i
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,21 @@
1
+ module PensioAPI
2
+ module Errors
3
+ class GatewayError < StandardError
4
+ include Mixins::HasTransactions
5
+ attr_reader :request_time, :request_path, :cardholder_message
6
+
7
+ def initialize(request)
8
+ super(request.body['MerchantErrorMessage'])
9
+
10
+ @raw = request.body
11
+
12
+ @request_time = Time.parse(request.headers['Date'])
13
+ @request_path = request.headers['Path']
14
+
15
+ @cardholder_message = request.body['CardHolderErrorMessage']
16
+
17
+ map_transactions
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ module PensioAPI
2
+ module Errors
3
+ class NoCredentials < StandardError
4
+ def initialize
5
+ super('Incomplete credentials were supplied')
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,30 @@
1
+ module PensioAPI
2
+ class FundingList
3
+ attr_reader :filename
4
+ attr_reader :amount
5
+ attr_reader :acquirer
6
+ attr_reader :funding_date
7
+ attr_reader :created_at
8
+ attr_reader :download_link
9
+
10
+ def self.all(options={})
11
+ request = Request.new('/merchant/API/fundingList', options)
12
+ Responses::FundingList.new(request)
13
+ end
14
+
15
+ def initialize(funding_list_body)
16
+ @raw = funding_list_body
17
+
18
+ @filename = @raw['Filename']
19
+ @amount = @raw['Amount']
20
+ @acquirer = @raw['Acquirer']
21
+ @funding_date = Date.parse(@raw['FundingDate'])
22
+ @created_at = Date.parse(@raw['CreatedDate'])
23
+ @download_link = @raw['DownloadLink']
24
+ end
25
+
26
+ def download
27
+ @result ||= FundingListRequest.new(@download_link).result
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,15 @@
1
+ require 'csv'
2
+
3
+ module PensioAPI
4
+ class FundingListRequest
5
+ include Mixins::RequestDefaults
6
+
7
+ attr_reader :result
8
+
9
+ def initialize(path, options={})
10
+ super(path, options)
11
+
12
+ @result = CSV.parse(@response.parsed_response, col_sep: ';', headers: true).reject(&:empty?)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,36 @@
1
+ module PensioAPI
2
+ module Mixins
3
+ module HasTransactions
4
+ def self.included(base)
5
+ base.send(:include, Enumerable)
6
+ base.send(:attr_reader, :transactions)
7
+ end
8
+
9
+ def each
10
+ @transactions.each { |t| yield t }
11
+ end
12
+
13
+ def last
14
+ @transactions.last
15
+ end
16
+
17
+ private
18
+
19
+ def map_transactions
20
+ @transactions = if raw_transactions.is_a?(Array)
21
+ raw_transactions.map { |t| PensioAPI::Transaction.new(t) }
22
+ else
23
+ [PensioAPI::Transaction.new(raw_transactions)]
24
+ end
25
+ end
26
+
27
+ def raw_transactions
28
+ @raw_transactions ||= if @raw['Transactions']
29
+ @raw['Transactions']['Transaction']
30
+ else
31
+ []
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,9 @@
1
+ module PensioAPI
2
+ module Mixins
3
+ module ID
4
+ def id
5
+ @raw["#{ self.class.name.demodulize }Id"]
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module PensioAPI
2
+ module Mixins
3
+ module MethodMissing
4
+ def method_missing(method, *args, &block)
5
+ @raw[method.to_s.camelize] || super
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,50 @@
1
+ module PensioAPI
2
+ module Mixins
3
+ module RequestDefaults
4
+ def self.included(base)
5
+ base.send(:include, HTTParty)
6
+ base.send(:attr_reader, :response)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ def set_base_uri
12
+ self.base_uri PensioAPI::Credentials.base_uri unless self.base_uri
13
+ end
14
+ end
15
+
16
+ HEADERS = {
17
+ 'Content-Type' => 'application/x-www-form-urlencoded'
18
+ }
19
+
20
+ def initialize(path, options={})
21
+ self.class.set_base_uri
22
+
23
+ raise Errors::NoCredentials unless credentials_supplied?
24
+
25
+ @response = self.class.post(path, request_options(options))
26
+ end
27
+
28
+ private
29
+
30
+ def request_options(options)
31
+ {
32
+ basic_auth: auth,
33
+ headers: (options.delete(:headers) || {}).merge(HEADERS),
34
+ body: options
35
+ }
36
+ end
37
+
38
+ def auth
39
+ {
40
+ username: PensioAPI::Credentials.username,
41
+ password: PensioAPI::Credentials.password
42
+ }
43
+ end
44
+
45
+ def credentials_supplied?
46
+ Credentials.base_uri && Credentials.username && Credentials.password
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,13 @@
1
+ module PensioAPI
2
+ module Mixins
3
+ module Timestamps
4
+ def created_at
5
+ Time.parse(@raw['CreatedDate'])
6
+ end
7
+
8
+ def updated_at
9
+ Time.parse(@raw['UpdatedDate'])
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ module PensioAPI
2
+ class Request
3
+ include Mixins::RequestDefaults
4
+
5
+ attr_reader :headers, :body
6
+
7
+ def initialize(path, options={})
8
+ super(path, options)
9
+
10
+ @headers = @response.parsed_response['APIResponse']['Header']
11
+ @body = @response.parsed_response['APIResponse']['Body']
12
+ end
13
+
14
+ def response_contains?(key)
15
+ @body && @body.has_key?(key)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,28 @@
1
+ module PensioAPI
2
+ class Reservation
3
+ def self.of_fixed_amount(options={})
4
+ request = Request.new('/merchant/API/reservationOfFixedAmount', options)
5
+ Responses::Reservation.new(request)
6
+ end
7
+
8
+ def initialize(transaction)
9
+ @transaction = transaction
10
+ end
11
+
12
+ def capture(options={})
13
+ request = Request.new('/merchant/API/captureReservation', options.merge(reservation_options))
14
+ Responses::ReservationCapture.new(request)
15
+ end
16
+
17
+ def release(options={})
18
+ request = Request.new('/merchant/API/releaseReservation', options.merge(reservation_options))
19
+ Responses::ReservationRelease.new(request)
20
+ end
21
+
22
+ private
23
+
24
+ def reservation_options
25
+ { transaction_id: @transaction.id }
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,32 @@
1
+ module PensioAPI
2
+ module Responses
3
+ class Base
4
+ include PensioAPI::Mixins::MethodMissing
5
+
6
+ attr_reader :raw
7
+
8
+ def initialize(request)
9
+ @raw = request.body
10
+ @headers = request.headers
11
+ unless success?
12
+ raise PensioAPI::Errors::BadRequest.new(request) unless header_ok
13
+ raise PensioAPI::Errors::GatewayError.new(request) unless body_ok
14
+ end
15
+ end
16
+
17
+ def success?
18
+ header_ok && body_ok
19
+ end
20
+
21
+ private
22
+
23
+ def header_ok
24
+ @headers['ErrorCode'].to_i == 0
25
+ end
26
+
27
+ def body_ok
28
+ !@raw.has_key?('Result') || ['Success', 'OK', nil].include?(@raw['Result'])
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,43 @@
1
+ module PensioAPI
2
+ module Responses
3
+ class FundingList < Base
4
+ attr_reader :funding_lists, :page_count
5
+
6
+ include Enumerable
7
+
8
+ def initialize(request)
9
+ super(request)
10
+
11
+ map_funding_lists
12
+
13
+ @page_count = if @raw['Fundings']
14
+ (@raw['Fundings']['numberOfPages'] || 0).to_i
15
+ else
16
+ 0
17
+ end
18
+ end
19
+
20
+ def each
21
+ @funding_lists.each { |fl| yield fl }
22
+ end
23
+
24
+ private
25
+
26
+ def map_funding_lists
27
+ @funding_lists = if raw_funding_lists.is_a?(Array)
28
+ raw_funding_lists.map { |fl| PensioAPI::FundingList.new(fl) }
29
+ else
30
+ [PensioAPI::FundingList.new(raw_funding_lists)]
31
+ end
32
+ end
33
+
34
+ def raw_funding_lists
35
+ @raw_funding_lists ||= if @raw['Fundings'] && raw['Fundings']['Funding']
36
+ @raw['Fundings']['Funding']
37
+ else
38
+ []
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,13 @@
1
+ module PensioAPI
2
+ module Responses
3
+ class GatewayURL < Base
4
+ attr_reader :url
5
+
6
+ def initialize(request)
7
+ super(request)
8
+
9
+ @url = @raw['Url']
10
+ end
11
+ end
12
+ end
13
+ end