buckaruby 1.0.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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