buckaruby 1.0.2 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rubocop.yml +20 -4
- data/.travis.yml +7 -3
- data/CHANGELOG.md +6 -0
- data/Gemfile +3 -1
- data/README.md +58 -4
- data/Rakefile +2 -0
- data/buckaruby.gemspec +1 -1
- data/lib/buckaruby.rb +3 -1
- data/lib/buckaruby/action.rb +3 -0
- data/lib/buckaruby/configuration.rb +73 -0
- data/lib/buckaruby/currency.rb +4 -0
- data/lib/buckaruby/exception.rb +26 -0
- data/lib/buckaruby/gateway.rb +99 -45
- data/lib/buckaruby/iban.rb +2 -0
- data/lib/buckaruby/ideal.rb +5 -2
- data/lib/buckaruby/language.rb +2 -0
- data/lib/buckaruby/operation.rb +4 -0
- data/lib/buckaruby/payment_method.rb +5 -1
- data/lib/buckaruby/request.rb +70 -27
- data/lib/buckaruby/response.rb +106 -37
- data/lib/buckaruby/signature.rb +8 -9
- data/lib/buckaruby/support/case_insensitive_hash.rb +2 -0
- data/lib/buckaruby/transaction_status.rb +2 -0
- data/lib/buckaruby/transaction_type.rb +2 -0
- data/lib/buckaruby/version.rb +3 -1
- data/spec/buckaruby/configuration_spec.rb +113 -0
- data/spec/buckaruby/gateway_spec.rb +165 -83
- data/spec/buckaruby/iban_spec.rb +2 -0
- data/spec/buckaruby/signature_spec.rb +38 -17
- data/spec/buckaruby/support/case_insensitive_hash_spec.rb +2 -0
- data/spec/fixtures/responses/callback_invalid_signature.txt +1 -0
- data/spec/fixtures/responses/callback_payment_cancelled.txt +1 -0
- data/spec/fixtures/responses/callback_payment_empty_transaction_type.txt +1 -0
- data/spec/fixtures/responses/callback_payment_failed.txt +1 -0
- data/spec/fixtures/responses/callback_payment_pending.txt +1 -0
- data/spec/fixtures/responses/callback_payment_rejected.txt +1 -0
- data/spec/fixtures/responses/callback_payment_sepa.txt +1 -0
- data/spec/fixtures/responses/callback_payment_success.txt +1 -0
- data/spec/fixtures/responses/callback_recurrent_sepa.txt +1 -0
- data/spec/fixtures/responses/callback_recurrent_visa.txt +1 -0
- data/spec/fixtures/responses/callback_refund_ideal.txt +1 -0
- data/spec/fixtures/responses/callback_refund_paypal.txt +1 -0
- data/spec/fixtures/responses/callback_reversal_paypal.txt +1 -0
- data/spec/fixtures/responses/callback_reversal_sepa.txt +1 -0
- data/spec/fixtures/responses/callback_valid_signature.txt +1 -0
- data/spec/fixtures/responses/cancel_success.txt +1 -0
- data/spec/fixtures/responses/recurrent_transaction_success.txt +1 -0
- data/spec/fixtures/responses/refund_info_error.txt +1 -0
- data/spec/fixtures/responses/refund_info_success.txt +1 -0
- data/spec/fixtures/responses/refund_transaction_success.txt +1 -0
- data/spec/fixtures/responses/setup_transaction_success.txt +1 -0
- data/spec/fixtures/responses/status_cancellable.txt +1 -0
- data/spec/fixtures/responses/status_noncancellable.txt +1 -0
- data/spec/fixtures/responses/status_success.txt +1 -0
- data/spec/spec_helper.rb +2 -0
- metadata +54 -4
- data/lib/buckaruby/urls.rb +0 -6
data/lib/buckaruby/iban.rb
CHANGED
data/lib/buckaruby/ideal.rb
CHANGED
@@ -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" => "
|
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
|
data/lib/buckaruby/language.rb
CHANGED
data/lib/buckaruby/operation.rb
CHANGED
@@ -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"
|
data/lib/buckaruby/request.rb
CHANGED
@@ -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(
|
13
|
-
@
|
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
|
-
|
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
|
-
|
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: @
|
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, @
|
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
|
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
|
data/lib/buckaruby/response.rb
CHANGED
@@ -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(
|
9
|
-
@
|
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
|
-
|
38
|
+
def timestamp
|
39
|
+
parse_time(params[:brq_timestamp])
|
12
40
|
end
|
13
41
|
|
14
42
|
private
|
15
43
|
|
16
|
-
def
|
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,
|
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
|
29
|
-
|
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
|
-
|
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
|
178
|
-
class
|
179
|
-
def initialize(response,
|
180
|
-
super(response,
|
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]
|
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 <
|
218
|
+
class SetupTransactionResponse < ApiResponse
|
219
|
+
include TransactionResponse
|
190
220
|
end
|
191
221
|
|
192
222
|
# Response when creating a recurrent transaction.
|
193
|
-
class RecurrentTransactionResponse <
|
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 <
|
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 <
|
269
|
+
class CallbackResponse < Response
|
270
|
+
include TransactionResponse
|
202
271
|
end
|
203
272
|
end
|