buckaruby 1.3.1 → 1.7.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.
@@ -2,8 +2,10 @@
2
2
 
3
3
  module Buckaruby
4
4
  # Helper for calculating the IBAN for a given account number, bank code and country code.
5
- class Iban
6
- def self.calculate_iban(account_number, bank_code, country_code = "NL")
5
+ module Iban
6
+ module_function
7
+
8
+ def calculate_iban(account_number, bank_code, country_code = "NL")
7
9
  if account_number.nil? || account_number.to_s.empty?
8
10
  raise ArgumentError, "Invalid account number"
9
11
  end
@@ -8,12 +8,13 @@ module Buckaruby
8
8
  "BUNQNL2A" => "bunq",
9
9
  "HANDNL2A" => "Handelsbanken",
10
10
  "INGBNL2A" => "ING",
11
- "KNABNL2H" => "Knab bank",
11
+ "KNABNL2H" => "Knab",
12
12
  "MOYONL21" => "Moneyou",
13
13
  "RABONL2U" => "Rabobank",
14
- "RBRBNL21" => "RegioBank",
14
+ "RBRBNL21" => "Regiobank",
15
+ "REVOLT21" => "Revolut",
15
16
  "SNSBNL2A" => "SNS Bank",
16
- "TRIONL2U" => "Triodos Bank",
17
+ "TRIONL2U" => "Triodos",
17
18
  "FVLBNL22" => "Van Lanschot"
18
19
  }.freeze
19
20
  end
@@ -4,6 +4,7 @@ module Buckaruby
4
4
  module Operation
5
5
  TRANSACTION_REQUEST = "TransactionRequest"
6
6
  TRANSACTION_STATUS = "TransactionStatus"
7
+ TRANSACTION_REQUEST_SPECIFICATION = "TransactionRequestSpecification"
7
8
  REFUND_INFO = "RefundInfo"
8
9
  CANCEL_TRANSACTION = "CancelTransaction"
9
10
  end
@@ -8,6 +8,8 @@ module Buckaruby
8
8
  SEPA_DIRECT_DEBIT = "sepadirectdebit"
9
9
  PAYPAL = "paypal"
10
10
  BANCONTACT_MISTER_CASH = "bancontactmrcash"
11
+ SOFORT = "sofortueberweisung"
12
+ GIROPAY = "giropay"
11
13
  TRANSFER = "transfer"
12
14
 
13
15
  # Credit cards
@@ -159,6 +159,30 @@ module Buckaruby
159
159
  end
160
160
  end
161
161
 
162
+ # Request for a creating a transaction specification.
163
+ class TransactionSpecificationRequest < Request
164
+ def execute(options)
165
+ super(options.merge(operation: Operation::TRANSACTION_REQUEST_SPECIFICATION))
166
+ end
167
+
168
+ def build_request_params(options)
169
+ params = {}
170
+
171
+ if options[:payment_method]
172
+ if options[:payment_method].respond_to?(:join)
173
+ params[:brq_services] = options[:payment_method].join(",")
174
+ else
175
+ params[:brq_services] = options[:payment_method]
176
+ end
177
+ end
178
+
179
+ params[:brq_latestversiononly] = "true"
180
+ params[:brq_culture] = options[:culture] || Language::DUTCH
181
+
182
+ params
183
+ end
184
+ end
185
+
162
186
  # Request for a creating a recurrent transaction.
163
187
  class RecurrentTransactionRequest < TransactionRequest
164
188
  def build_transaction_request_params(options)
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'support/case_insensitive_hash'
4
+
3
5
  require 'cgi'
4
6
  require 'date'
5
7
 
@@ -20,19 +22,7 @@ module Buckaruby
20
22
  end
21
23
 
22
24
  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
25
+ TransactionStatus.parse(params[:brq_statuscode])
36
26
  end
37
27
 
38
28
  def timestamp
@@ -83,7 +73,7 @@ module Buckaruby
83
73
  end
84
74
 
85
75
  def verify_signature!(response, config)
86
- if params[:brq_apiresult] != "Fail"
76
+ if params[:brq_apiresult].nil? || params[:brq_apiresult].casecmp("fail") != 0
87
77
  sent_signature = params[:brq_signature]
88
78
  generated_signature = Signature.generate_signature(response, config)
89
79
 
@@ -174,76 +164,7 @@ module Buckaruby
174
164
  end
175
165
 
176
166
  def transaction_type
177
- if params[:brq_transaction_type] && !params[:brq_transaction_type].empty?
178
- # See http://support.buckaroo.nl/index.php/Transactietypes
179
- case params[:brq_transaction_type]
180
- when 'C021', 'V021', # iDEAL
181
- 'C002', 'C004', 'C005', # (SEPA) Direct Debit
182
- 'V010', 'V014', # PayPal
183
- 'C090', 'V090', # Bancontact
184
- 'C001', # Transfer
185
-
186
- 'C044', 'C192', 'C283', 'C293', 'C318', 'C345',
187
- 'C880', 'C963', 'V002', 'V032', 'V038', 'V044',
188
- 'V192', 'V283', 'V293', 'V313', 'V318', 'V345',
189
- 'V696', # Visa
190
-
191
- 'C043', 'C089', 'C273', 'C303', 'C328', 'C355',
192
- 'C876', 'C969', 'V001', 'V031', 'V037', 'V043',
193
- 'V089', 'V273', 'V303', 'V328', 'V355', 'V702', # MasterCard
194
-
195
- 'C046', 'C251', 'C288', 'C308', 'C333', 'C872',
196
- 'C972', 'V034', 'V040', 'V046', 'V094', 'V245',
197
- 'V288', 'V308', 'V333', 'V705', # Maestro
198
-
199
- 'V003', 'V030', 'V036', 'V042' # American Express
200
-
201
- # Check the recurring flag to detect a normal or recurring transaction.
202
- if params[:brq_recurring] && params[:brq_recurring].casecmp("true").zero?
203
- TransactionType::PAYMENT_RECURRENT
204
- else
205
- TransactionType::PAYMENT
206
- end
207
- when 'C121', # iDEAL
208
- 'C102', 'C500', # (SEPA) Direct Debit
209
- 'V110', # PayPal
210
- 'C092', 'V092', # Bancontact
211
- 'C101', # Transfer
212
-
213
- 'C080', 'C194', 'C281', 'C290', 'C315', 'C342',
214
- 'C881', 'C961', 'V068', 'V074', 'V080', 'V102',
215
- 'V194', 'V281', 'V290', 'V315', 'V342', 'V694', # Visa
216
-
217
- 'C079', 'C197', 'C300', 'C325', 'C352', 'C371',
218
- 'C877', 'C967', 'V067', 'V073', 'V079', 'V101',
219
- 'V149', 'V197', 'V300', 'V325', 'V352', 'V371',
220
- 'V700', # MasterCard
221
-
222
- 'C082', 'C252', 'C286', 'C305', 'C330', 'C873',
223
- 'C970', 'V070', 'V076', 'V082', 'V246', 'V286',
224
- 'V305', 'V330', 'V703', # Maestro
225
-
226
- 'V066', 'V072', 'V078', 'V103' # American Express
227
- TransactionType::REFUND
228
- when 'C501', 'C502', 'C562', # (SEPA) Direct Debit
229
- 'V111', # PayPal
230
-
231
- 'C554', 'C593', 'C882', 'V132', 'V138', 'V144',
232
- 'V544', 'V592', # Visa
233
-
234
- 'C553', 'C589', 'C878', 'V131', 'V137', 'V143',
235
- 'V543', 'V589', # MasterCard
236
-
237
- 'C546', 'C551', 'C874', 'V134', 'V140', 'V146',
238
- 'V545', 'V546', # Maestro
239
-
240
- 'V130', 'V136', 'V142' # American Express
241
- TransactionType::REVERSAL
242
- end
243
- else
244
- # Fallback when transaction type is not known (cancelling credit card)
245
- TransactionType::PAYMENT
246
- end
167
+ TransactionType.parse(params[:brq_transaction_type], params[:brq_recurring])
247
168
  end
248
169
 
249
170
  def transaction_status
@@ -348,4 +269,19 @@ module Buckaruby
348
269
  class CallbackResponse < Response
349
270
  include TransactionResponse
350
271
  end
272
+
273
+ # Response when retrieving the specification for a transaction.
274
+ class TransactionSpecificationResponse < ApiResponse
275
+ def services
276
+ @services ||= FieldMapper.map_fields(params, :brq_services)
277
+ end
278
+
279
+ def basic_fields
280
+ @basic_fields ||= FieldMapper.map_fields(params, :brq_basicfields)
281
+ end
282
+
283
+ def custom_parameters
284
+ @custom_parameters ||= FieldMapper.map_fields(params, :brq_customparameters)
285
+ end
286
+ end
351
287
  end
@@ -5,8 +5,10 @@ require 'digest'
5
5
  module Buckaruby
6
6
  # Calculate a signature based on the parameters of the payment request or response.
7
7
  # -> see BPE 3.0 Gateway NVP, chapter 4 'Digital Signature'
8
- class Signature
9
- def self.generate_signature(params, config)
8
+ module Signature
9
+ module_function
10
+
11
+ def generate_signature(params, config)
10
12
  case config.hash_method
11
13
  when :sha1
12
14
  Digest::SHA1.hexdigest(generate_signature_string(params, config.secret))
@@ -19,11 +21,27 @@ module Buckaruby
19
21
  end
20
22
  end
21
23
 
22
- def self.generate_signature_string(params, secret)
24
+ def generate_signature_string(params, secret)
23
25
  sign_params = params.select { |key, _value| key.to_s.upcase.start_with?("BRQ_", "ADD_", "CUST_") && key.to_s.casecmp("BRQ_SIGNATURE").nonzero? }
24
- string = sign_params.sort_by { |p| p.first.downcase }.map { |param| "#{param[0]}=#{param[1]}" }.join
26
+ sign_params = order_signature_params(sign_params)
27
+
28
+ string = sign_params.map { |param| "#{param[0]}=#{param[1]}" }.join
25
29
  string << secret
26
30
  string
27
31
  end
32
+
33
+ # Excerpt from the Buckaroo documentation, chapter 4 'Digital Signature':
34
+ # In the payment engine, the used lexical sort algorithm uses the following order:
35
+ # symbols first, then numbers, then case insensitive letters. Also, a shorter string
36
+ # that is identical to the beginning of a longer string, comes before the longer string.
37
+ # Take for example the following, comma separated, list which has been sorted:
38
+ # a_a, a0, a0a, a1a, aaA, aab, aba, aCa
39
+ CHAR_ORDER = "_01234567890abcdefghijklmnopqrstuvwxyz"
40
+
41
+ def order_signature_params(params)
42
+ params.sort_by do |key, _value|
43
+ key.to_s.downcase.each_char.map { |c| CHAR_ORDER.index(c) }
44
+ end
45
+ end
28
46
  end
29
47
  end
@@ -1,11 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Buckaruby
4
+ # Parses the transaction status code from Buckaroo.
4
5
  module TransactionStatus
5
6
  SUCCESS = 1
6
7
  FAILED = 2
7
8
  REJECTED = 3
8
9
  CANCELLED = 4
9
10
  PENDING = 5
11
+
12
+ module_function
13
+
14
+ # See https://support.buckaroo.nl/categorie%C3%ABn/transacties/status
15
+ def parse(brq_statuscode)
16
+ case brq_statuscode
17
+ when '190'
18
+ TransactionStatus::SUCCESS
19
+ when '490', '491', '492'
20
+ TransactionStatus::FAILED
21
+ when '690'
22
+ TransactionStatus::REJECTED
23
+ when '790', '791', '792', '793'
24
+ TransactionStatus::PENDING
25
+ when '890', '891'
26
+ TransactionStatus::CANCELLED
27
+ end
28
+ end
10
29
  end
11
30
  end
@@ -1,10 +1,91 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Buckaruby
4
+ # Parses the transaction type from Buckaroo.
4
5
  module TransactionType
5
6
  PAYMENT = 1
6
7
  PAYMENT_RECURRENT = 2
7
8
  REFUND = 3
8
9
  REVERSAL = 4
10
+
11
+ module_function
12
+
13
+ # See https://support.buckaroo.nl/categorie%C3%ABn/integratie/transactietypes-overzicht
14
+ def parse(brq_transaction_type, brq_recurring)
15
+ if brq_transaction_type && !brq_transaction_type.empty?
16
+ case brq_transaction_type
17
+ when 'C021', 'V021', # iDEAL
18
+ 'C002', 'C004', 'C005', # (SEPA) Direct Debit
19
+ 'V010', 'V014', # PayPal
20
+ 'C090', 'V090', # Bancontact
21
+ 'C075', 'N074', # Sofort
22
+ 'C241', 'N031', # Giropay
23
+ 'C001', # Transfer
24
+
25
+ 'C044', 'C192', 'C283', 'C293', 'C318', 'C345',
26
+ 'C880', 'C963', 'V002', 'V032', 'V038', 'V044',
27
+ 'V192', 'V283', 'V293', 'V313', 'V318', 'V345',
28
+ 'V696', # Visa
29
+
30
+ 'C043', 'C089', 'C273', 'C303', 'C328', 'C355',
31
+ 'C876', 'C969', 'V001', 'V031', 'V037', 'V043',
32
+ 'V089', 'V273', 'V303', 'V328', 'V355', 'V702', # MasterCard
33
+
34
+ 'C046', 'C251', 'C288', 'C308', 'C333', 'C872',
35
+ 'C972', 'V034', 'V040', 'V046', 'V094', 'V245',
36
+ 'V288', 'V308', 'V333', 'V705', # Maestro
37
+
38
+ 'V003', 'V030', 'V036', 'V042' # American Express
39
+
40
+ # Check the recurring flag to detect a normal or recurring transaction.
41
+ if brq_recurring && brq_recurring.casecmp("true").zero?
42
+ TransactionType::PAYMENT_RECURRENT
43
+ else
44
+ TransactionType::PAYMENT
45
+ end
46
+ when 'C121', # iDEAL
47
+ 'C102', 'C500', # (SEPA) Direct Debit
48
+ 'V110', # PayPal
49
+ 'C092', 'V092', # Bancontact
50
+ 'C543', 'N540', # Sofort
51
+ 'C242', 'N541', # Giropay
52
+ 'C101', # Transfer
53
+
54
+ 'C080', 'C194', 'C281', 'C290', 'C315', 'C342',
55
+ 'C881', 'C961', 'V068', 'V074', 'V080', 'V102',
56
+ 'V194', 'V281', 'V290', 'V315', 'V342', 'V694', # Visa
57
+
58
+ 'C079', 'C197', 'C300', 'C325', 'C352', 'C371',
59
+ 'C877', 'C967', 'V067', 'V073', 'V079', 'V101',
60
+ 'V149', 'V197', 'V300', 'V325', 'V352', 'V371',
61
+ 'V700', # MasterCard
62
+
63
+ 'C082', 'C252', 'C286', 'C305', 'C330', 'C873',
64
+ 'C970', 'V070', 'V076', 'V082', 'V246', 'V286',
65
+ 'V305', 'V330', 'V703', # Maestro
66
+
67
+ 'V066', 'V072', 'V078', 'V103' # American Express
68
+ TransactionType::REFUND
69
+ when 'C501', 'C502', 'C562', # (SEPA) Direct Debit
70
+ 'V111', # PayPal
71
+ 'C544', # Sofort
72
+
73
+ 'C554', 'C593', 'C882', 'V132', 'V138', 'V144',
74
+ 'V544', 'V592', # Visa
75
+
76
+ 'C553', 'C589', 'C878', 'V131', 'V137', 'V143',
77
+ 'V543', 'V589', # MasterCard
78
+
79
+ 'C546', 'C551', 'C874', 'V134', 'V140', 'V146',
80
+ 'V545', 'V546', # Maestro
81
+
82
+ 'V130', 'V136', 'V142' # American Express
83
+ TransactionType::REVERSAL
84
+ end
85
+ else
86
+ # Fallback when transaction type is not known (e.g. cancelling credit card)
87
+ TransactionType::PAYMENT
88
+ end
89
+ end
9
90
  end
10
91
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Buckaruby
4
- VERSION = "1.3.1"
4
+ VERSION = "1.7.0"
5
5
  end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Buckaruby::FieldMapper do
6
+ describe '.map_fields' do
7
+ let(:fields) do
8
+ {
9
+ brq_services_1_description: "iDEAL",
10
+ brq_services_1_name: "ideal",
11
+ brq_services_1_version: "2",
12
+ brq_services_1_supportedcurrencies_1_code: "EUR",
13
+ brq_services_1_supportedcurrencies_1_isonumber: "978",
14
+ brq_services_1_supportedcurrencies_1_name: "Euro",
15
+ brq_services_2_description: "Visa",
16
+ brq_services_2_name: "visa",
17
+ brq_services_2_version: "1",
18
+ brq_services_2_supportedcurrencies_1_code: "GBP",
19
+ brq_services_2_supportedcurrencies_1_isonumber: "826",
20
+ brq_services_2_supportedcurrencies_1_name: "Pond",
21
+ brq_services_2_supportedcurrencies_2_code: "USD",
22
+ brq_services_2_supportedcurrencies_2_isonumber: "840",
23
+ brq_services_2_supportedcurrencies_2_name: "US Dollar"
24
+ }
25
+ end
26
+
27
+ it 'maps NVP fields to arrays and hashes' do
28
+ services = Buckaruby::FieldMapper.map_fields(fields, :brq_services)
29
+ expect(services).to be_an_instance_of(Array)
30
+ expect(services.count).to eq(2)
31
+
32
+ ideal = services.first
33
+ expect(ideal).to be_an_instance_of(Buckaruby::Support::CaseInsensitiveHash)
34
+ expect(ideal[:description]).to eq("iDEAL")
35
+ expect(ideal[:name]).to eq("ideal")
36
+ expect(ideal[:version]).to eq("2")
37
+
38
+ visa = services.last
39
+ expect(visa).to be_an_instance_of(Buckaruby::Support::CaseInsensitiveHash)
40
+ expect(visa[:description]).to eq("Visa")
41
+ expect(visa[:name]).to eq("visa")
42
+ expect(visa[:version]).to eq("1")
43
+ end
44
+
45
+ it 'maps NVP subfields to arrays and hashes' do
46
+ services = Buckaruby::FieldMapper.map_fields(fields, :brq_services)
47
+
48
+ currencies = services.first[:supportedcurrencies]
49
+ expect(currencies).to be_an_instance_of(Array)
50
+ expect(currencies.count).to eq(1)
51
+
52
+ currency = currencies.first
53
+ expect(currency).to be_an_instance_of(Buckaruby::Support::CaseInsensitiveHash)
54
+ expect(currency[:code]).to eq("EUR")
55
+ expect(currency[:isonumber]).to eq("978")
56
+ expect(currency[:name]).to eq("Euro")
57
+
58
+ currencies = services.last[:supportedcurrencies]
59
+ expect(currencies).to be_an_instance_of(Array)
60
+ expect(currencies.count).to eq(2)
61
+
62
+ currency = currencies.first
63
+ expect(currency).to be_an_instance_of(Buckaruby::Support::CaseInsensitiveHash)
64
+ expect(currency[:code]).to eq("GBP")
65
+ expect(currency[:isonumber]).to eq("826")
66
+ expect(currency[:name]).to eq("Pond")
67
+ end
68
+ end
69
+ end