buckaruby 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ca0648f0a6575163b3684b909a613114b6f7634d
4
+ data.tar.gz: 58d4b4baf6dbf494a39839352c24c522a415ea87
5
+ SHA512:
6
+ metadata.gz: dfc7886aaa82c537c786ed16543abd0934d28529e1c40a76709699c6f4d9f3ee49b7caca09aac5c79de733b88397cfac1a29209a3f2df6960df2fb1227c5519f
7
+ data.tar.gz: dacded44324296a0af0c05063047d87f6764bd613ab438091995caf7abcb6d80b25a1d06235b6283da0a1701940c7f061a11ce7cd4d3395be72d10c5302ff418
data/.gitignore ADDED
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .rvmrc
7
+ .ruby-version
8
+ .ruby-gemset
9
+ Gemfile.lock
10
+ InstalledFiles
11
+ _yardoc
12
+ coverage
13
+ doc/
14
+ lib/bundler/man
15
+ pkg
16
+ rdoc
17
+ spec/reports
18
+ test/tmp
19
+ test/version_tmp
20
+ tmp
data/.rubocop.yml ADDED
@@ -0,0 +1,49 @@
1
+ # Buckaruby RuboCop configuration
2
+
3
+ AllCops:
4
+ TargetRubyVersion: 2.2
5
+ DisplayCopNames: true
6
+ DisplayStyleGuide: true
7
+
8
+ Metrics/AbcSize:
9
+ Max: 30
10
+
11
+ Metrics/ClassLength:
12
+ Enabled: false
13
+
14
+ Metrics/CyclomaticComplexity:
15
+ Max: 8
16
+
17
+ Metrics/LineLength:
18
+ Enabled: false
19
+
20
+ Metrics/MethodLength:
21
+ Enabled: false
22
+
23
+ Metrics/PerceivedComplexity:
24
+ Max: 8
25
+
26
+ Style/AccessorMethodName:
27
+ Enabled: false
28
+
29
+ Style/BlockDelimiters:
30
+ Exclude:
31
+ - 'spec/**/*.rb'
32
+
33
+ Style/ConditionalAssignment:
34
+ Enabled: false
35
+
36
+ Style/GuardClause:
37
+ Enabled: false
38
+
39
+ Style/IfUnlessModifier:
40
+ Enabled: false
41
+
42
+ Style/MutableConstant:
43
+ Enabled: false
44
+
45
+ Style/RedundantReturn:
46
+ Enabled: false
47
+
48
+ Style/StringLiterals:
49
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ sudo: false
3
+ cache: bundler
4
+ rvm:
5
+ - 2.2.5
6
+ - 2.3.1
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in buckaruby.gemspec
4
+ gemspec
5
+
6
+ gem 'rubocop', '~> 0.45.0', group: :test
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Kentaa
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,137 @@
1
+ # Buckaruby
2
+
3
+ [![Build Status](https://travis-ci.org/KentaaNL/buckaruby.svg?branch=master)](https://travis-ci.org/KentaaNL/buckaruby)
4
+
5
+ The Buckaruby gem provides a Ruby library for communicating with the Buckaroo Payment Engine 3.0.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'buckaruby'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install buckaruby
20
+
21
+ ## Usage
22
+
23
+ Configure Buckaruby to use it in test or production (live) mode:
24
+
25
+ ```ruby
26
+ Buckaruby::Gateway.mode = :production # defaults to :test
27
+ ```
28
+
29
+ ### Initialization
30
+
31
+ Create the gateway and configure it using your Buckaroo website key and secret key:
32
+
33
+ ```ruby
34
+ gateway = Buckaruby::Gateway.new(
35
+ website: "123456789", # website key
36
+ secret: "abcdef1234567890" # secret key for digital signing
37
+ )
38
+ ```
39
+
40
+ As hashing method for the digital signature, Buckaruby uses SHA-1 by default. You can change this to SHA-256 or SHA-512 by setting the parameter `hash_method` when creating the gateway:
41
+
42
+ ```ruby
43
+ gateway = Buckaruby::Gateway.new(
44
+ website: "123456789",
45
+ secret: "abcdef1234567890",
46
+ hash_method: :sha512 # hash method for the digital signature (:sha1, :sha256 or :sha512)
47
+ )
48
+ ```
49
+
50
+ ### Start transaction
51
+
52
+ To start a new transaction, use the method `setup_transaction`:
53
+
54
+ ```ruby
55
+ options = {
56
+ amount: 10,
57
+ payment_method: Buckaruby::PaymentMethod::IDEAL,
58
+ payment_issuer: Buckaruby::Ideal::ISSUERS.keys.first,
59
+ invoicenumber: "12345",
60
+ return_url: "http://www.return.url/"
61
+ }
62
+
63
+ response = gateway.setup_transaction(options)
64
+ ```
65
+
66
+ On success this will return a `Buckaruby::SetupTransactionResponse`.
67
+
68
+ ### Recurrent transaction
69
+
70
+ Recurrent transactions are supported for all credit cards, PayPal and SEPA Direct Debit.
71
+
72
+ You first need to execute a normal transaction, with the parameter `recurring` set to true.
73
+
74
+ ```ruby
75
+ options = {
76
+ amount: 10,
77
+ payment_method: Buckaruby::PaymentMethod::PAYPAL,
78
+ invoicenumber: "12345",
79
+ return_url: "http://www.return.url/",
80
+ recurring: true
81
+ }
82
+
83
+ response = gateway.setup_transaction(options)
84
+ ```
85
+
86
+ The response will include a `transaction_id` which you must use to make a recurrent transaction:
87
+
88
+ ```ruby
89
+ options = {
90
+ amount: 10,
91
+ payment_method: Buckaruby::PaymentMethod::PAYPAL,
92
+ invoicenumber: "12345",
93
+ transaction_id: "abcdefg"
94
+ }
95
+
96
+ response = gateway.recurrent_transaction(options)
97
+ ```
98
+
99
+ On success this will return a `Buckaruby::RecurrentTransactionResponse`.
100
+
101
+ ### Push response
102
+
103
+ Buckaroo can be configured to send push notifications for transactions. You can use the method `callback` to verify and parse the push response:
104
+
105
+ ```ruby
106
+ response = gateway.callback(params)
107
+ ```
108
+
109
+ On success this will return a `Buckaruby::CallbackResponse`.
110
+
111
+ ### Get status
112
+
113
+ To query the status of any transaction, use the method `status` with either the parameter `transaction_id` or `payment_id`:
114
+
115
+ ```ruby
116
+ response = gateway.status(transaction_id: 12345)
117
+ ```
118
+
119
+ On success this will return a `Buckaruby::StatusResponse`.
120
+
121
+ ### Error handling
122
+
123
+ When missing or invalid parameters are passed to any method, an `ArgumentError` will be raised.
124
+
125
+ When a request to Buckaroo fails because of connection problems, a `Buckaruby::ConnectionException` will be raised.
126
+
127
+ When an API call to Buckaroo results in a "Fail" returned, a `Buckaruby::ApiException` will be raised.
128
+
129
+ When the signature could not be verified, a `Buckaruby::SignatureException` will be raised.
130
+
131
+ ## Contributing
132
+
133
+ Bug reports and pull requests are welcome on GitHub at https://github.com/KentaaNL/buckaruby.
134
+
135
+ ## License
136
+
137
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require "rubocop/rake_task"
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+ RuboCop::RakeTask.new(:rubocop)
7
+
8
+ task default: [:spec, :rubocop]
data/buckaruby.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'buckaruby/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "buckaruby"
8
+ spec.version = Buckaruby::VERSION
9
+ spec.authors = ["Kentaa"]
10
+ spec.email = ["support@kentaa.nl"]
11
+ spec.summary = "Ruby library for communicating with the Buckaroo Payment Engine 3.0."
12
+ spec.description = "The Buckaruby gem provides a Ruby library for communicating with the Buckaroo Payment Engine 3.0."
13
+ spec.homepage = "http://www.buckaroo.nl/"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^spec/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.required_ruby_version = ">= 2.0.0"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.7"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "rspec", "~> 3.5.0", ">= 3.5.0"
26
+ end
@@ -0,0 +1,7 @@
1
+ module Buckaruby
2
+ module Action
3
+ PAY = "Pay"
4
+ PAY_RECURRENT = "PayRecurrent"
5
+ EXTRA_INFO = "ExtraInfo"
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ module Buckaruby
2
+ module Currency
3
+ EURO = "EUR"
4
+ end
5
+ end
@@ -0,0 +1,54 @@
1
+ module Buckaruby
2
+ # Base class for exceptions.
3
+ class BuckarooException < StandardError
4
+ end
5
+
6
+ # Exception raised when an API call to Buckaroo results in a "Fail".
7
+ class ApiException < BuckarooException
8
+ attr_reader :params
9
+
10
+ def initialize(params = {})
11
+ @params = params
12
+
13
+ if status_message && status_code
14
+ message = "API request failed: #{status_message} (#{status_code})"
15
+ elsif error_message
16
+ message = "API request failed: #{error_message}"
17
+ else
18
+ message = "API request failed"
19
+ end
20
+
21
+ super(message)
22
+ end
23
+
24
+ def status_message
25
+ params[:brq_statusmessage]
26
+ end
27
+
28
+ def status_code
29
+ params[:brq_statuscode]
30
+ end
31
+
32
+ def error_message
33
+ params[:brq_apierrormessage]
34
+ end
35
+ end
36
+
37
+ # Exception raised when a request to Buckaroo fails because of connection problems.
38
+ class ConnectionException < BuckarooException
39
+ def initialize(exception)
40
+ message = "Error connecting to Buckaroo: #{exception.message} (#{exception.class})"
41
+
42
+ super(message)
43
+ end
44
+ end
45
+
46
+ # Exception raised when the signature could not be verified.
47
+ class SignatureException < BuckarooException
48
+ def initialize(sent_signature = "", generated_signature = "")
49
+ message = "Sent signature (#{sent_signature}) doesn't match generated signature (#{generated_signature})"
50
+
51
+ super(message)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,204 @@
1
+ require 'bigdecimal'
2
+ require 'logger'
3
+
4
+ module Buckaruby
5
+ # Implementation of the BPE 3.0 NVP Gateway.
6
+ class Gateway
7
+ class << self
8
+ # Buckaroo mode can be set as class setting
9
+ attr_accessor :mode
10
+ end
11
+
12
+ attr_reader :options
13
+
14
+ def initialize(options = {})
15
+ validate_required_params!(options, :website, :secret)
16
+
17
+ @logger = defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
18
+ @options = options
19
+
20
+ set_buckaroo_mode!(@options)
21
+ set_hash_method!(@options)
22
+ end
23
+
24
+ # Get a list with payment issuers.
25
+ def issuers(payment_method)
26
+ if payment_method != PaymentMethod::IDEAL
27
+ raise ArgumentError, "Invalid payment method, only #{PaymentMethod::IDEAL} is supported."
28
+ end
29
+
30
+ return Ideal::ISSUERS
31
+ end
32
+
33
+ # Setup a new transaction.
34
+ def setup_transaction(options = {})
35
+ @logger.debug("[setup_transaction] options=#{options.inspect}")
36
+
37
+ validate_setup_transaction_params!(options)
38
+
39
+ normalize_account_iban!(options) if options[:payment_method] == PaymentMethod::SEPA_DIRECT_DEBIT
40
+
41
+ request = build_request(:setup_transaction)
42
+ response = request.execute(options)
43
+
44
+ return SetupTransactionResponse.new(response, @options)
45
+ end
46
+
47
+ # Setup a recurrent transaction.
48
+ def recurrent_transaction(options = {})
49
+ @logger.debug("[recurrent_transaction] options=#{options.inspect}")
50
+
51
+ validate_recurrent_transaction_params!(options)
52
+
53
+ request = build_request(:recurrent_transaction)
54
+ response = request.execute(options)
55
+
56
+ return RecurrentTransactionResponse.new(response, @options)
57
+ end
58
+
59
+ # Get transaction status.
60
+ def status(options = {})
61
+ @logger.debug("[status] options=#{options.inspect}")
62
+
63
+ validate_status_params!(options)
64
+
65
+ request = build_request(:status)
66
+ response = request.execute(options)
67
+
68
+ return StatusResponse.new(response, @options)
69
+ end
70
+
71
+ # Verify the response / callback.
72
+ def callback(response = {})
73
+ if response.empty?
74
+ raise ArgumentError, "No callback parameters found"
75
+ end
76
+
77
+ return CallbackResponse.new(response, @options)
78
+ end
79
+
80
+ private
81
+
82
+ # Validate required parameters.
83
+ def validate_required_params!(params, *required)
84
+ required.flatten.each do |param|
85
+ if !params.key?(param) || params[param].to_s.empty?
86
+ raise ArgumentError, "Missing required parameter: #{param}."
87
+ end
88
+ end
89
+ end
90
+
91
+ # Validate params for setup transaction.
92
+ def validate_setup_transaction_params!(options)
93
+ required_params = [:amount, :payment_method, :invoicenumber]
94
+ required_params << :return_url if options[:payment_method] != PaymentMethod::SEPA_DIRECT_DEBIT
95
+
96
+ case options[:payment_method]
97
+ when PaymentMethod::IDEAL
98
+ required_params << :payment_issuer
99
+ when PaymentMethod::SEPA_DIRECT_DEBIT
100
+ required_params << [:account_iban, :account_name]
101
+ end
102
+
103
+ validate_required_params!(options, required_params)
104
+
105
+ validate_amount!(options)
106
+
107
+ valid_payment_methods = [
108
+ PaymentMethod::IDEAL, PaymentMethod::VISA, PaymentMethod::MASTER_CARD, PaymentMethod::MAESTRO,
109
+ PaymentMethod::SEPA_DIRECT_DEBIT, PaymentMethod::PAYPAL, PaymentMethod::BANCONTACT_MISTER_CASH
110
+ ]
111
+ validate_payment_method!(options, valid_payment_methods)
112
+
113
+ validate_payment_issuer!(options)
114
+ end
115
+
116
+ # Validate amount of money, must be greater than 0.
117
+ def validate_amount!(options)
118
+ money = BigDecimal.new(options[:amount].to_s)
119
+ if money <= 0
120
+ raise ArgumentError, "Invalid amount: #{options[:amount]} (must be greater than 0)"
121
+ end
122
+ end
123
+
124
+ # Validate the payment method.
125
+ def validate_payment_method!(options, valid)
126
+ unless valid.include?(options[:payment_method])
127
+ raise ArgumentError, "Invalid payment method: #{options[:payment_method]}"
128
+ end
129
+ end
130
+
131
+ # Validate the payment issuer when iDEAL is selected as payment method.
132
+ def validate_payment_issuer!(options)
133
+ if options[:payment_method] == PaymentMethod::IDEAL
134
+ unless Ideal::ISSUERS.include?(options[:payment_issuer])
135
+ raise ArgumentError, "Invalid payment issuer: #{options[:payment_issuer]}"
136
+ end
137
+ end
138
+ end
139
+
140
+ # Validate params for recurrent transaction.
141
+ def validate_recurrent_transaction_params!(options)
142
+ required_params = [:amount, :payment_method, :invoicenumber, :transaction_id]
143
+
144
+ validate_required_params!(options, required_params)
145
+
146
+ validate_amount!(options)
147
+
148
+ valid_payment_methods = [
149
+ PaymentMethod::VISA, PaymentMethod::MASTER_CARD, PaymentMethod::MAESTRO,
150
+ PaymentMethod::SEPA_DIRECT_DEBIT, PaymentMethod::PAYPAL
151
+ ]
152
+ validate_payment_method!(options, valid_payment_methods)
153
+ end
154
+
155
+ # Validate params for transaction status.
156
+ def validate_status_params!(options)
157
+ if !options[:transaction_id] && !options[:payment_id]
158
+ raise ArgumentError, "Missing parameters: transaction_id or payment_id should be present"
159
+ end
160
+ end
161
+
162
+ # Strip spaces from the IBAN.
163
+ def normalize_account_iban!(options)
164
+ iban = options[:account_iban].to_s.gsub(/\s/, "")
165
+
166
+ options[:account_iban] = iban
167
+ end
168
+
169
+ # Set Buckaroo mode from options, class setting or the default (test).
170
+ def set_buckaroo_mode!(options)
171
+ mode = options.key?(:mode) ? options[:mode] : Gateway.mode
172
+ mode ||= :test
173
+
174
+ if mode != :test && mode != :production
175
+ raise ArgumentError, "Invalid Buckaroo mode provided: #{mode} (expected :test or :production)"
176
+ end
177
+
178
+ options[:mode] = mode
179
+ end
180
+
181
+ # Set the hash method from options or default (SHA-1).
182
+ def set_hash_method!(options)
183
+ hash_method = (options[:hash_method] || "SHA1").downcase.to_sym
184
+
185
+ unless [:sha1, :sha256, :sha512].include?(hash_method)
186
+ raise ArgumentError, "Invalid hash method provided: #{options[:hash_method]} (expected :sha1, :sha256 or :sha512)"
187
+ end
188
+
189
+ options[:hash_method] = hash_method
190
+ end
191
+
192
+ # Factory method for constructing a request.
193
+ def build_request(request_type)
194
+ case request_type
195
+ when :setup_transaction
196
+ SetupTransactionRequest.new(@options)
197
+ when :recurrent_transaction
198
+ RecurrentTransactionRequest.new(@options)
199
+ when :status
200
+ StatusRequest.new(@options)
201
+ end
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,25 @@
1
+ module Buckaruby
2
+ # Helper for calculating the IBAN for a given account number, bank code and country code.
3
+ class Iban
4
+ def self.calculate_iban(account_number, bank_code, country_code = "NL")
5
+ if account_number.nil? || account_number.to_s.empty?
6
+ raise ArgumentError, "Invalid account number"
7
+ end
8
+
9
+ if bank_code.nil? || bank_code.to_s.empty?
10
+ raise ArgumentError, "Invalid bank code"
11
+ end
12
+
13
+ if country_code.nil? || !country_code.match(/^[A-Z]{2}$/)
14
+ raise ArgumentError, "Invalid or unknown country code"
15
+ end
16
+
17
+ account_identification = bank_code + account_number.rjust(10, '0')
18
+ country_code_added = account_identification + country_code
19
+ all_numbers = country_code_added.gsub(/[A-Z]/) { |p| (p.respond_to?(:ord) ? p.ord : p[0]) - 55 } + '00'
20
+ verification_number = (98 - (all_numbers.to_i % 97)).to_s.rjust(2, '0')
21
+
22
+ return country_code + verification_number + account_identification
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,16 @@
1
+ module Buckaruby
2
+ module Ideal
3
+ ISSUERS = {
4
+ "ABNANL2A" => "ABN AMRO",
5
+ "ASNBNL21" => "ASN Bank",
6
+ "BUNQNL2A" => "Bunq",
7
+ "INGBNL2A" => "ING",
8
+ "KNABNL2H" => "Knab bank",
9
+ "RABONL2U" => "Rabobank",
10
+ "RBRBNL21" => "RegioBank",
11
+ "SNSBNL2A" => "SNS Bank",
12
+ "TRIONL2U" => "Triodos Bank",
13
+ "FVLBNL22" => "Van Lanschot"
14
+ }
15
+ end
16
+ end
@@ -0,0 +1,8 @@
1
+ module Buckaruby
2
+ module Language
3
+ DUTCH = 'nl-NL'
4
+ ENGLISH = 'en-GB'
5
+ FRENCH = 'fr-FR'
6
+ DEUTSCH = 'de-DE'
7
+ end
8
+ end
@@ -0,0 +1,6 @@
1
+ module Buckaruby
2
+ module Operation
3
+ TRANSACTION_REQUEST = "TransactionRequest"
4
+ TRANSACTION_STATUS = "TransactionStatus"
5
+ end
6
+ end
@@ -0,0 +1,14 @@
1
+ module Buckaruby
2
+ module PaymentMethod
3
+ IDEAL = "ideal"
4
+ SEPA_DIRECT_DEBIT = "sepadirectdebit"
5
+ PAYPAL = "paypal"
6
+ BANCONTACT_MISTER_CASH = "bancontactmrcash"
7
+ TRANSFER = "transfer"
8
+
9
+ # Credit cards
10
+ VISA = "visa"
11
+ MASTER_CARD = "mastercard"
12
+ MAESTRO = "maestro"
13
+ end
14
+ end