buckaruby 1.0.2 → 1.1.0

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 (58) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +20 -4
  3. data/.travis.yml +7 -3
  4. data/CHANGELOG.md +6 -0
  5. data/Gemfile +3 -1
  6. data/README.md +58 -4
  7. data/Rakefile +2 -0
  8. data/buckaruby.gemspec +1 -1
  9. data/lib/buckaruby.rb +3 -1
  10. data/lib/buckaruby/action.rb +3 -0
  11. data/lib/buckaruby/configuration.rb +73 -0
  12. data/lib/buckaruby/currency.rb +4 -0
  13. data/lib/buckaruby/exception.rb +26 -0
  14. data/lib/buckaruby/gateway.rb +99 -45
  15. data/lib/buckaruby/iban.rb +2 -0
  16. data/lib/buckaruby/ideal.rb +5 -2
  17. data/lib/buckaruby/language.rb +2 -0
  18. data/lib/buckaruby/operation.rb +4 -0
  19. data/lib/buckaruby/payment_method.rb +5 -1
  20. data/lib/buckaruby/request.rb +70 -27
  21. data/lib/buckaruby/response.rb +106 -37
  22. data/lib/buckaruby/signature.rb +8 -9
  23. data/lib/buckaruby/support/case_insensitive_hash.rb +2 -0
  24. data/lib/buckaruby/transaction_status.rb +2 -0
  25. data/lib/buckaruby/transaction_type.rb +2 -0
  26. data/lib/buckaruby/version.rb +3 -1
  27. data/spec/buckaruby/configuration_spec.rb +113 -0
  28. data/spec/buckaruby/gateway_spec.rb +165 -83
  29. data/spec/buckaruby/iban_spec.rb +2 -0
  30. data/spec/buckaruby/signature_spec.rb +38 -17
  31. data/spec/buckaruby/support/case_insensitive_hash_spec.rb +2 -0
  32. data/spec/fixtures/responses/callback_invalid_signature.txt +1 -0
  33. data/spec/fixtures/responses/callback_payment_cancelled.txt +1 -0
  34. data/spec/fixtures/responses/callback_payment_empty_transaction_type.txt +1 -0
  35. data/spec/fixtures/responses/callback_payment_failed.txt +1 -0
  36. data/spec/fixtures/responses/callback_payment_pending.txt +1 -0
  37. data/spec/fixtures/responses/callback_payment_rejected.txt +1 -0
  38. data/spec/fixtures/responses/callback_payment_sepa.txt +1 -0
  39. data/spec/fixtures/responses/callback_payment_success.txt +1 -0
  40. data/spec/fixtures/responses/callback_recurrent_sepa.txt +1 -0
  41. data/spec/fixtures/responses/callback_recurrent_visa.txt +1 -0
  42. data/spec/fixtures/responses/callback_refund_ideal.txt +1 -0
  43. data/spec/fixtures/responses/callback_refund_paypal.txt +1 -0
  44. data/spec/fixtures/responses/callback_reversal_paypal.txt +1 -0
  45. data/spec/fixtures/responses/callback_reversal_sepa.txt +1 -0
  46. data/spec/fixtures/responses/callback_valid_signature.txt +1 -0
  47. data/spec/fixtures/responses/cancel_success.txt +1 -0
  48. data/spec/fixtures/responses/recurrent_transaction_success.txt +1 -0
  49. data/spec/fixtures/responses/refund_info_error.txt +1 -0
  50. data/spec/fixtures/responses/refund_info_success.txt +1 -0
  51. data/spec/fixtures/responses/refund_transaction_success.txt +1 -0
  52. data/spec/fixtures/responses/setup_transaction_success.txt +1 -0
  53. data/spec/fixtures/responses/status_cancellable.txt +1 -0
  54. data/spec/fixtures/responses/status_noncancellable.txt +1 -0
  55. data/spec/fixtures/responses/status_success.txt +1 -0
  56. data/spec/spec_helper.rb +2 -0
  57. metadata +54 -4
  58. data/lib/buckaruby/urls.rb +0 -6
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Buckaruby
2
4
  # Helper for calculating the IBAN for a given account number, bank code and country code.
3
5
  class Iban
@@ -1,16 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Buckaruby
2
4
  module Ideal
3
5
  ISSUERS = {
4
6
  "ABNANL2A" => "ABN AMRO",
5
7
  "ASNBNL21" => "ASN Bank",
6
- "BUNQNL2A" => "Bunq",
8
+ "BUNQNL2A" => "bunq",
7
9
  "INGBNL2A" => "ING",
8
10
  "KNABNL2H" => "Knab bank",
11
+ "MOYONL21" => "Moneyou",
9
12
  "RABONL2U" => "Rabobank",
10
13
  "RBRBNL21" => "RegioBank",
11
14
  "SNSBNL2A" => "SNS Bank",
12
15
  "TRIONL2U" => "Triodos Bank",
13
16
  "FVLBNL22" => "Van Lanschot"
14
- }
17
+ }.freeze
15
18
  end
16
19
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Buckaruby
2
4
  module Language
3
5
  DUTCH = 'nl-NL'
@@ -1,6 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Buckaruby
2
4
  module Operation
3
5
  TRANSACTION_REQUEST = "TransactionRequest"
4
6
  TRANSACTION_STATUS = "TransactionStatus"
7
+ REFUND_INFO = "RefundInfo"
8
+ CANCEL_TRANSACTION = "CancelTransaction"
5
9
  end
6
10
  end
@@ -1,6 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Buckaruby
2
4
  module PaymentMethod
3
- IDEAL = "ideal"
5
+ IDEAL = "ideal" # iDEAL collecting
6
+ IDEAL_PROCESSING = "idealprocessing" # iDEAL processing
7
+
4
8
  SEPA_DIRECT_DEBIT = "sepadirectdebit"
5
9
  PAYPAL = "paypal"
6
10
  BANCONTACT_MISTER_CASH = "bancontactmrcash"
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bigdecimal'
2
4
  require 'cgi'
3
5
  require 'date'
4
- require 'logger'
5
6
  require 'net/http'
6
7
  require 'openssl'
7
8
  require 'uri'
@@ -9,20 +10,18 @@ require 'uri'
9
10
  module Buckaruby
10
11
  # Base class for any request.
11
12
  class Request
12
- def initialize(options)
13
- @options = options
14
-
15
- @logger = defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
13
+ def initialize(config)
14
+ @config = config
15
+ @logger = config.logger
16
16
  end
17
17
 
18
18
  def execute(options)
19
- uri = URI.parse(api_url)
19
+ uri = URI.parse(@config.api_url)
20
20
  uri.query = "op=#{options[:operation]}" if options[:operation]
21
21
 
22
- raw_response = post_buckaroo(uri, build_request_data(options))
23
- response = parse_response(raw_response)
22
+ response = post_buckaroo(uri, build_request_data(options))
24
23
 
25
- @logger.debug("[execute] response: #{response.inspect}")
24
+ # @logger.debug("[execute] response: #{response.inspect}")
26
25
 
27
26
  response
28
27
  end
@@ -46,7 +45,8 @@ module Buckaruby
46
45
  raise InvalidResponseException, raw_response
47
46
  end
48
47
 
49
- return raw_response.body
48
+ raw_response.body
49
+
50
50
  # Try to catch some common exceptions Net::HTTP might raise
51
51
  rescue Errno::ETIMEDOUT, Errno::EINVAL, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EHOSTUNREACH,
52
52
  IOError, SocketError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::OpenTimeout,
@@ -55,14 +55,14 @@ module Buckaruby
55
55
  end
56
56
 
57
57
  def build_request_data(options)
58
- params = { brq_websitekey: @options[:website] }
58
+ params = { brq_websitekey: @config.website }
59
59
 
60
60
  params.merge!(build_request_params(options))
61
61
 
62
62
  params[:add_buckaruby] = "Buckaruby #{Buckaruby::VERSION}"
63
63
 
64
64
  # Sign the data with our secret key.
65
- params[:brq_signature] = Signature.generate_signature(params, @options)
65
+ params[:brq_signature] = Signature.generate_signature(params, @config)
66
66
 
67
67
  params
68
68
  end
@@ -70,20 +70,6 @@ module Buckaruby
70
70
  def post_data(params)
71
71
  params.map { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
72
72
  end
73
-
74
- def parse_response(body)
75
- query = CGI.parse(body)
76
- query.each { |key, value| query[key] = value.first }
77
- query
78
- end
79
-
80
- def test?
81
- @options[:mode] == :test
82
- end
83
-
84
- def api_url
85
- test? ? Urls::TEST_URL : Urls::PRODUCTION_URL
86
- end
87
73
  end
88
74
 
89
75
  # Base class for a transaction request.
@@ -97,7 +83,7 @@ module Buckaruby
97
83
  brq_payment_method: options[:payment_method],
98
84
  brq_culture: options[:culture] || Language::DUTCH,
99
85
  brq_currency: options[:currency] || Currency::EURO,
100
- brq_amount: BigDecimal.new(options[:amount].to_s).to_s("F"),
86
+ brq_amount: BigDecimal(options[:amount].to_s).to_s("F"),
101
87
  brq_invoicenumber: options[:invoicenumber]
102
88
  }
103
89
 
@@ -127,6 +113,12 @@ module Buckaruby
127
113
  brq_service_ideal_issuer: options[:payment_issuer],
128
114
  brq_service_ideal_version: "2"
129
115
  )
116
+ when PaymentMethod::IDEAL_PROCESSING
117
+ params.merge!(
118
+ brq_service_idealprocessing_action: Action::PAY,
119
+ brq_service_idealprocessing_issuer: options[:payment_issuer],
120
+ brq_service_idealprocessing_version: "2"
121
+ )
130
122
  when PaymentMethod::SEPA_DIRECT_DEBIT
131
123
  params.merge!(
132
124
  brq_service_sepadirectdebit_action: Action::PAY,
@@ -175,6 +167,44 @@ module Buckaruby
175
167
  end
176
168
  end
177
169
 
170
+ # Request for a creating a refund.
171
+ class RefundTransactionRequest < Request
172
+ def execute(options)
173
+ super(options.merge(operation: Operation::TRANSACTION_REQUEST))
174
+ end
175
+
176
+ def build_request_params(options)
177
+ params = {
178
+ brq_payment_method: options[:payment_method],
179
+ brq_amount_credit: BigDecimal(options[:amount].to_s).to_s("F"),
180
+ brq_currency: options[:currency] || Currency::EURO,
181
+ brq_invoicenumber: options[:invoicenumber]
182
+ }
183
+
184
+ key = :"brq_service_#{options[:payment_method]}_action"
185
+ params[key] = Action::REFUND
186
+
187
+ params[:brq_originaltransaction] = options[:transaction_id]
188
+
189
+ params
190
+ end
191
+ end
192
+
193
+ # Request for retrieving refund information.
194
+ class RefundInfoRequest < Request
195
+ def execute(options)
196
+ super(options.merge(operation: Operation::REFUND_INFO))
197
+ end
198
+
199
+ def build_request_params(options)
200
+ params = {}
201
+
202
+ params[:brq_transaction] = options[:transaction_id]
203
+
204
+ params
205
+ end
206
+ end
207
+
178
208
  # Request for getting the status of a transaction.
179
209
  class StatusRequest < Request
180
210
  def execute(options)
@@ -190,4 +220,17 @@ module Buckaruby
190
220
  params
191
221
  end
192
222
  end
223
+
224
+ # Request for cancelling a transaction.
225
+ class CancelRequest < Request
226
+ def execute(options)
227
+ super(options.merge(operation: Operation::CANCEL_TRANSACTION))
228
+ end
229
+
230
+ def build_request_params(options)
231
+ params = { brq_transaction: options[:transaction_id] }
232
+
233
+ params
234
+ end
235
+ end
193
236
  end
@@ -1,3 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cgi'
1
4
  require 'date'
2
5
 
3
6
  module Buckaruby
@@ -5,32 +8,74 @@ module Buckaruby
5
8
  class Response
6
9
  attr_reader :params
7
10
 
8
- def initialize(response, options)
9
- @params = Support::CaseInsensitiveHash.new(response)
11
+ def initialize(body, config)
12
+ @logger = config.logger
13
+
14
+ @response = parse_response(body)
15
+ @params = Support::CaseInsensitiveHash.new(@response)
16
+
17
+ @logger.debug("[response] params: #{params.inspect}")
18
+
19
+ verify_signature!(@response, config)
20
+ end
21
+
22
+ def status
23
+ # See http://support.buckaroo.nl/index.php/Statuscodes
24
+ case params[:brq_statuscode]
25
+ when '190'
26
+ TransactionStatus::SUCCESS
27
+ when '490', '491', '492'
28
+ TransactionStatus::FAILED
29
+ when '690'
30
+ TransactionStatus::REJECTED
31
+ when '790', '791', '792', '793'
32
+ TransactionStatus::PENDING
33
+ when '890', '891'
34
+ TransactionStatus::CANCELLED
35
+ end
36
+ end
10
37
 
11
- verify_signature!(response, options)
38
+ def timestamp
39
+ parse_time(params[:brq_timestamp])
12
40
  end
13
41
 
14
42
  private
15
43
 
16
- def verify_signature!(response, options)
44
+ def parse_response(body)
45
+ if body.is_a?(Hash)
46
+ response = body
47
+ else
48
+ response = CGI.parse(body)
49
+ response.each { |key, value| response[key] = value.first }
50
+ end
51
+
52
+ response
53
+ end
54
+
55
+ def verify_signature!(response, config)
17
56
  if params[:brq_apiresult] != "Fail"
18
57
  sent_signature = params[:brq_signature]
19
- generated_signature = Signature.generate_signature(response, options)
58
+ generated_signature = Signature.generate_signature(response, config)
20
59
 
21
60
  if sent_signature != generated_signature
22
61
  raise SignatureException.new(sent_signature, generated_signature)
23
62
  end
24
63
  end
25
64
  end
65
+
66
+ def parse_time(time)
67
+ time ? Time.strptime(time, '%Y-%m-%d %H:%M:%S') : nil
68
+ end
26
69
  end
27
70
 
28
- # Base class for a transaction response.
29
- class TransactionResponse < Response
71
+ # Base for a transaction response.
72
+ module TransactionResponse
30
73
  def account_bic
31
74
  case payment_method
32
75
  when PaymentMethod::IDEAL
33
76
  params[:brq_service_ideal_consumerbic]
77
+ when PaymentMethod::IDEAL_PROCESSING
78
+ params[:brq_service_idealprocessing_consumerbic]
34
79
  when PaymentMethod::SEPA_DIRECT_DEBIT
35
80
  params[:brq_service_sepadirectdebit_customerbic]
36
81
  end
@@ -40,6 +85,8 @@ module Buckaruby
40
85
  case payment_method
41
86
  when PaymentMethod::IDEAL
42
87
  params[:brq_service_ideal_consumeriban]
88
+ when PaymentMethod::IDEAL_PROCESSING
89
+ params[:brq_service_idealprocessing_consumeriban]
43
90
  when PaymentMethod::SEPA_DIRECT_DEBIT
44
91
  params[:brq_service_sepadirectdebit_customeriban]
45
92
  end
@@ -49,6 +96,8 @@ module Buckaruby
49
96
  case payment_method
50
97
  when PaymentMethod::IDEAL
51
98
  params[:brq_service_ideal_consumername] || params[:brq_customer_name]
99
+ when PaymentMethod::IDEAL_PROCESSING
100
+ params[:brq_service_idealprocessing_consumername] || params[:brq_customer_name]
52
101
  when PaymentMethod::SEPA_DIRECT_DEBIT
53
102
  params[:brq_service_sepadirectdebit_customername] || params[:brq_customer_name]
54
103
  end
@@ -90,10 +139,6 @@ module Buckaruby
90
139
  params[:brq_relatedtransaction_reversal]
91
140
  end
92
141
 
93
- def timestamp
94
- parse_time(params[:brq_timestamp])
95
- end
96
-
97
142
  def transaction_id
98
143
  params[:brq_transactions]
99
144
  end
@@ -123,19 +168,7 @@ module Buckaruby
123
168
  end
124
169
 
125
170
  def transaction_status
126
- # See http://support.buckaroo.nl/index.php/Statuscodes
127
- case params[:brq_statuscode]
128
- when '190'
129
- TransactionStatus::SUCCESS
130
- when '490', '491', '492'
131
- TransactionStatus::FAILED
132
- when '690'
133
- TransactionStatus::REJECTED
134
- when '790', '791', '792', '793'
135
- TransactionStatus::PENDING
136
- when '890', '891'
137
- TransactionStatus::CANCELLED
138
- end
171
+ status
139
172
  end
140
173
 
141
174
  def to_h
@@ -165,39 +198,75 @@ module Buckaruby
165
198
  date ? Date.strptime(date, '%Y-%m-%d') : nil
166
199
  end
167
200
 
168
- def parse_time(time)
169
- time ? Time.strptime(time, '%Y-%m-%d %H:%M:%S') : nil
170
- end
171
-
172
201
  def parse_payment_method(method)
173
202
  method ? method.downcase : nil
174
203
  end
175
204
  end
176
205
 
177
- # Base class for a transaction response via the API.
178
- class TransactionApiResponse < TransactionResponse
179
- def initialize(response, options)
180
- super(response, options)
206
+ # Base class for a response via the API.
207
+ class ApiResponse < Response
208
+ def initialize(response, config)
209
+ super(response, config)
181
210
 
182
- if params[:brq_apiresult].nil? || params[:brq_apiresult] == "Fail"
211
+ if params[:brq_apiresult].nil? || params[:brq_apiresult].casecmp("fail").zero?
183
212
  raise ApiException, params
184
213
  end
185
214
  end
186
215
  end
187
216
 
188
217
  # Response when creating a new transaction.
189
- class SetupTransactionResponse < TransactionApiResponse
218
+ class SetupTransactionResponse < ApiResponse
219
+ include TransactionResponse
190
220
  end
191
221
 
192
222
  # Response when creating a recurrent transaction.
193
- class RecurrentTransactionResponse < TransactionApiResponse
223
+ class RecurrentTransactionResponse < ApiResponse
224
+ include TransactionResponse
225
+ end
226
+
227
+ # Response when creating a refund transaction.
228
+ class RefundTransactionResponse < ApiResponse
229
+ include TransactionResponse
230
+ end
231
+
232
+ # Response when retrieving the refund information.
233
+ class RefundInfoResponse < ApiResponse
234
+ def payment_method
235
+ params[:brq_refundinfo_1_servicecode]
236
+ end
237
+
238
+ def refundable?
239
+ !params[:brq_refundinfo_1_isrefundable].nil? && params[:brq_refundinfo_1_isrefundable].casecmp("true").zero?
240
+ end
241
+
242
+ def maximum_amount
243
+ params[:brq_refundinfo_1_maximumrefundamount]
244
+ end
245
+
246
+ def invoicenumber
247
+ params[:brq_refundinfo_1_invoice]
248
+ end
249
+
250
+ def currency
251
+ params[:brq_refundinfo_1_refundcurrency]
252
+ end
194
253
  end
195
254
 
196
255
  # Response when getting the status of a transaction.
197
- class StatusResponse < TransactionApiResponse
256
+ class StatusResponse < ApiResponse
257
+ include TransactionResponse
258
+
259
+ def cancellable?
260
+ !params[:brq_transaction_cancelable].nil? && params[:brq_transaction_cancelable].casecmp("true").zero?
261
+ end
262
+ end
263
+
264
+ # Response when cancelling a transaction.
265
+ class CancelResponse < ApiResponse
198
266
  end
199
267
 
200
268
  # Response when verifying the Buckaroo callback.
201
- class CallbackResponse < TransactionResponse
269
+ class CallbackResponse < Response
270
+ include TransactionResponse
202
271
  end
203
272
  end