pensio_api 0.2.1 → 0.2.2

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.
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